File: | .cache/bazel/_bazel_alan/39be661231df2a680c9b74265384c13c/execroot/org_tensorflow/tensorflow/python/eager/pywrap_tfe_src.cc |
Warning: | line 495, column 31 PyObject ownership leak with reference count of 1 |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 <atomic> | ||||
17 | #include <cstring> | ||||
18 | #include <unordered_map> | ||||
19 | |||||
20 | #include "absl/debugging/leak_check.h" | ||||
21 | #include "absl/strings/str_cat.h" | ||||
22 | #include "absl/types/variant.h" | ||||
23 | #include "tensorflow/c/c_api.h" | ||||
24 | #include "tensorflow/c/c_api_internal.h" | ||||
25 | #include "tensorflow/c/eager/c_api.h" | ||||
26 | #include "tensorflow/c/eager/c_api_internal.h" | ||||
27 | #include "tensorflow/c/eager/tape.h" | ||||
28 | #include "tensorflow/c/eager/tfe_context_internal.h" | ||||
29 | #include "tensorflow/c/eager/tfe_op_internal.h" | ||||
30 | #include "tensorflow/c/eager/tfe_tensorhandle_internal.h" | ||||
31 | #include "tensorflow/c/tf_status.h" | ||||
32 | #include "tensorflow/core/framework/types.pb.h" | ||||
33 | #include "tensorflow/core/lib/core/errors.h" | ||||
34 | #include "tensorflow/core/lib/gtl/cleanup.h" | ||||
35 | #include "tensorflow/core/lib/gtl/compactptrset.h" | ||||
36 | #include "tensorflow/core/lib/gtl/flatmap.h" | ||||
37 | #include "tensorflow/core/lib/gtl/flatset.h" | ||||
38 | #include "tensorflow/core/lib/strings/strcat.h" | ||||
39 | #include "tensorflow/core/lib/strings/stringprintf.h" | ||||
40 | #include "tensorflow/core/platform/casts.h" | ||||
41 | #include "tensorflow/core/platform/errors.h" | ||||
42 | #include "tensorflow/core/platform/mutex.h" | ||||
43 | #include "tensorflow/core/platform/protobuf.h" | ||||
44 | #include "tensorflow/core/platform/status.h" | ||||
45 | #include "tensorflow/core/platform/types.h" | ||||
46 | #include "tensorflow/core/profiler/lib/traceme.h" | ||||
47 | #include "tensorflow/core/util/managed_stack_trace.h" | ||||
48 | #include "tensorflow/python/eager/pywrap_gradient_exclusions.h" | ||||
49 | #include "tensorflow/python/eager/pywrap_tensor.h" | ||||
50 | #include "tensorflow/python/eager/pywrap_tfe.h" | ||||
51 | #include "tensorflow/python/lib/core/py_util.h" | ||||
52 | #include "tensorflow/python/lib/core/safe_ptr.h" | ||||
53 | #include "tensorflow/python/util/stack_trace.h" | ||||
54 | #include "tensorflow/python/util/util.h" | ||||
55 | |||||
56 | using tensorflow::Status; | ||||
57 | using tensorflow::string; | ||||
58 | using tensorflow::strings::Printf; | ||||
59 | |||||
60 | namespace { | ||||
61 | // NOTE: Items are retrieved from and returned to these unique_ptrs, and they | ||||
62 | // act as arenas. This is important if the same thread requests 2 items without | ||||
63 | // releasing one. | ||||
64 | // The following sequence of events on the same thread will still succeed: | ||||
65 | // - GetOp <- Returns existing. | ||||
66 | // - GetOp <- Allocates and returns a new pointer. | ||||
67 | // - ReleaseOp <- Sets the item in the unique_ptr. | ||||
68 | // - ReleaseOp <- Sets the item in the unique_ptr, deleting the old one. | ||||
69 | // This occurs when a PyFunc kernel is run. This behavior makes it safe in that | ||||
70 | // case, as well as the case where python decides to reuse the underlying | ||||
71 | // C++ thread in 2 python threads case. | ||||
72 | struct OpDeleter { | ||||
73 | void operator()(TFE_Op* op) const { TFE_DeleteOp(op); } | ||||
74 | }; | ||||
75 | thread_local std::unordered_map<TFE_Context*, | ||||
76 | std::unique_ptr<TFE_Op, OpDeleter>> | ||||
77 | thread_local_eager_operation_map; // NOLINT | ||||
78 | thread_local std::unique_ptr<TF_Status> thread_local_tf_status = // NOLINT | ||||
79 | nullptr; | ||||
80 | |||||
81 | std::unique_ptr<TFE_Op, OpDeleter> ReleaseThreadLocalOp(TFE_Context* ctx) { | ||||
82 | auto it = thread_local_eager_operation_map.find(ctx); | ||||
83 | if (it == thread_local_eager_operation_map.end()) { | ||||
84 | return nullptr; | ||||
85 | } | ||||
86 | return std::move(it->second); | ||||
87 | } | ||||
88 | |||||
89 | TFE_Op* GetOp(TFE_Context* ctx, const char* op_or_function_name, | ||||
90 | const char* raw_device_name, TF_Status* status) { | ||||
91 | auto op = ReleaseThreadLocalOp(ctx); | ||||
92 | if (!op) { | ||||
93 | op.reset(tensorflow::wrap(tensorflow::unwrap(ctx)->CreateOperation())); | ||||
94 | } | ||||
95 | status->status = | ||||
96 | tensorflow::unwrap(op.get())->Reset(op_or_function_name, raw_device_name); | ||||
97 | if (!status->status.ok()) { | ||||
98 | op.reset(); | ||||
99 | } | ||||
100 | return op.release(); | ||||
101 | } | ||||
102 | |||||
103 | void ReturnOp(TFE_Context* ctx, TFE_Op* op) { | ||||
104 | if (op) { | ||||
105 | tensorflow::unwrap(op)->Clear(); | ||||
106 | thread_local_eager_operation_map[ctx].reset(op); | ||||
107 | } | ||||
108 | } | ||||
109 | |||||
110 | TF_Status* ReleaseThreadLocalStatus() { | ||||
111 | if (thread_local_tf_status == nullptr) { | ||||
112 | return nullptr; | ||||
113 | } | ||||
114 | return thread_local_tf_status.release(); | ||||
115 | } | ||||
116 | |||||
117 | struct InputInfo { | ||||
118 | InputInfo(int i, bool is_list) : i(i), is_list(is_list) {} | ||||
119 | |||||
120 | int i; | ||||
121 | bool is_list = false; | ||||
122 | }; | ||||
123 | |||||
124 | // Takes in output gradients, returns input gradients. | ||||
125 | typedef std::function<PyObject*(PyObject*, const std::vector<int64_t>&)> | ||||
126 | PyBackwardFunction; | ||||
127 | |||||
128 | using AttrToInputsMap = | ||||
129 | tensorflow::gtl::FlatMap<string, | ||||
130 | tensorflow::gtl::InlinedVector<InputInfo, 4>>; | ||||
131 | |||||
132 | tensorflow::gtl::FlatMap<string, AttrToInputsMap*>* GetAllAttrToInputsMaps() { | ||||
133 | static auto* all_attr_to_input_maps = | ||||
134 | new tensorflow::gtl::FlatMap<string, AttrToInputsMap*>; | ||||
135 | return all_attr_to_input_maps; | ||||
136 | } | ||||
137 | |||||
138 | // This function doesn't use a lock, since we depend on the GIL directly. | ||||
139 | AttrToInputsMap* GetAttrToInputsMapHoldingGIL(const tensorflow::OpDef& op_def) { | ||||
140 | #if PY_MAJOR_VERSION3 >= 3 && PY_MINOR_VERSION8 >= 4 | ||||
141 | DCHECK(PyGILState_Check())while (false && (PyGILState_Check())) ::tensorflow::internal ::LogMessageFatal("tensorflow/python/eager/pywrap_tfe_src.cc" , 141) | ||||
142 | << "This function needs to hold the GIL when called."; | ||||
143 | #endif | ||||
144 | auto* all_attr_to_input_maps = GetAllAttrToInputsMaps(); | ||||
145 | auto* output = | ||||
146 | tensorflow::gtl::FindPtrOrNull(*all_attr_to_input_maps, op_def.name()); | ||||
147 | if (output != nullptr) { | ||||
148 | return output; | ||||
149 | } | ||||
150 | |||||
151 | std::unique_ptr<AttrToInputsMap> m(new AttrToInputsMap); | ||||
152 | |||||
153 | // Store a list of InputIndex -> List of corresponding inputs. | ||||
154 | for (int i = 0; i < op_def.input_arg_size(); i++) { | ||||
155 | if (!op_def.input_arg(i).type_attr().empty()) { | ||||
156 | auto it = m->find(op_def.input_arg(i).type_attr()); | ||||
157 | if (it == m->end()) { | ||||
158 | it = m->insert({op_def.input_arg(i).type_attr(), {}}).first; | ||||
159 | } | ||||
160 | it->second.emplace_back(i, !op_def.input_arg(i).number_attr().empty()); | ||||
161 | } | ||||
162 | } | ||||
163 | |||||
164 | auto* retval = m.get(); | ||||
165 | (*all_attr_to_input_maps)[op_def.name()] = m.release(); | ||||
166 | |||||
167 | return retval; | ||||
168 | } | ||||
169 | |||||
170 | // This function doesn't use a lock, since we depend on the GIL directly. | ||||
171 | tensorflow::gtl::FlatMap< | ||||
172 | string, tensorflow::gtl::FlatMap<string, tensorflow::DataType>*>* | ||||
173 | GetAllAttrToDefaultsMaps() { | ||||
174 | static auto* all_attr_to_defaults_maps = new tensorflow::gtl::FlatMap< | ||||
175 | string, tensorflow::gtl::FlatMap<string, tensorflow::DataType>*>; | ||||
176 | return all_attr_to_defaults_maps; | ||||
177 | } | ||||
178 | |||||
179 | tensorflow::gtl::FlatMap<string, tensorflow::DataType>* | ||||
180 | GetAttrToDefaultsMapHoldingGIL(const tensorflow::OpDef& op_def) { | ||||
181 | #if PY_MAJOR_VERSION3 >= 3 && PY_MINOR_VERSION8 >= 4 | ||||
182 | DCHECK(PyGILState_Check())while (false && (PyGILState_Check())) ::tensorflow::internal ::LogMessageFatal("tensorflow/python/eager/pywrap_tfe_src.cc" , 182) | ||||
183 | << "This function needs to hold the GIL when called."; | ||||
184 | #endif | ||||
185 | auto* all_attr_to_defaults_maps = GetAllAttrToDefaultsMaps(); | ||||
186 | auto* output = | ||||
187 | tensorflow::gtl::FindPtrOrNull(*all_attr_to_defaults_maps, op_def.name()); | ||||
188 | if (output != nullptr) { | ||||
189 | return output; | ||||
190 | } | ||||
191 | |||||
192 | auto* new_map = new tensorflow::gtl::FlatMap<string, tensorflow::DataType>; | ||||
193 | |||||
194 | for (const auto& attr : op_def.attr()) { | ||||
195 | if (attr.type() == "type" && attr.has_default_value()) { | ||||
196 | new_map->insert({attr.name(), attr.default_value().type()}); | ||||
197 | } | ||||
198 | } | ||||
199 | |||||
200 | (*all_attr_to_defaults_maps)[op_def.name()] = new_map; | ||||
201 | |||||
202 | return new_map; | ||||
203 | } | ||||
204 | |||||
205 | struct FastPathOpExecInfo { | ||||
206 | TFE_Context* ctx; | ||||
207 | const char* device_name; | ||||
208 | |||||
209 | bool run_callbacks; | ||||
210 | bool run_post_exec_callbacks; | ||||
211 | bool run_gradient_callback; | ||||
212 | |||||
213 | // The op name of the main op being executed. | ||||
214 | PyObject* name; | ||||
215 | // The op type name of the main op being executed. | ||||
216 | PyObject* op_name; | ||||
217 | PyObject* callbacks; | ||||
218 | |||||
219 | // All the args passed into the FastPathOpExecInfo. | ||||
220 | PyObject* args; | ||||
221 | |||||
222 | // DTypes can come from another input that has the same attr. So build that | ||||
223 | // map. | ||||
224 | const AttrToInputsMap* attr_to_inputs_map; | ||||
225 | const tensorflow::gtl::FlatMap<string, tensorflow::DataType>* default_dtypes; | ||||
226 | tensorflow::gtl::FlatMap<string, tensorflow::DataType> cached_dtypes; | ||||
227 | }; | ||||
228 | |||||
229 | #define PARSE_VALUE(fn_name, type, check_fn, parse_fn) \ | ||||
230 | bool fn_name(const string& key, PyObject* py_value, TF_Status* status, \ | ||||
231 | type* value) { \ | ||||
232 | if (check_fn(py_value)) { \ | ||||
233 | *value = static_cast<type>(parse_fn(py_value)); \ | ||||
234 | return true; \ | ||||
235 | } else { \ | ||||
236 | TF_SetStatus(status, TF_INVALID_ARGUMENT, \ | ||||
237 | tensorflow::strings::StrCat( \ | ||||
238 | "Expecting " #type " value for attr ", key, ", got ", \ | ||||
239 | py_value->ob_type->tp_name) \ | ||||
240 | .c_str()); \ | ||||
241 | return false; \ | ||||
242 | } \ | ||||
243 | } | ||||
244 | |||||
245 | #if PY_MAJOR_VERSION3 >= 3 | ||||
246 | PARSE_VALUE(ParseIntValue, int, PyLong_Check, PyLong_AsLong) | ||||
247 | PARSE_VALUE(ParseInt64Value, int64_t, PyLong_Check, PyLong_AsLongLong) | ||||
248 | #else | ||||
249 | PARSE_VALUE(ParseIntValue, int, PyInt_Check, PyInt_AsLong) | ||||
250 | #endif | ||||
251 | PARSE_VALUE(ParseFloatValue, float, PyFloat_Check, PyFloat_AsDouble) | ||||
252 | #undef PARSE_VALUE | ||||
253 | |||||
254 | #if PY_MAJOR_VERSION3 < 3 | ||||
255 | bool ParseInt64Value(const string& key, PyObject* py_value, TF_Status* status, | ||||
256 | int64_t* value) { | ||||
257 | if (PyInt_Check(py_value)) { | ||||
258 | *value = static_cast<int64_t>(PyInt_AsLong(py_value)); | ||||
259 | return true; | ||||
260 | } else if (PyLong_Check(py_value)((((((PyObject*)(py_value))->ob_type))->tp_flags & ( (1UL << 24))) != 0)) { | ||||
261 | *value = static_cast<int64_t>(PyLong_AsLong(py_value)); | ||||
262 | return true; | ||||
263 | } | ||||
264 | TF_SetStatus( | ||||
265 | status, TF_INVALID_ARGUMENT, | ||||
266 | tensorflow::strings::StrCat("Expecting int or long value for attr ", key, | ||||
267 | ", got ", py_value->ob_type->tp_name) | ||||
268 | .c_str()); | ||||
269 | return false; | ||||
270 | } | ||||
271 | #endif | ||||
272 | |||||
273 | Py_ssize_t TensorShapeNumDims(PyObject* value) { | ||||
274 | const auto size = PySequence_Size(value); | ||||
275 | if (size == -1) { | ||||
276 | // TensorShape.__len__ raises an error in the scenario where the shape is an | ||||
277 | // unknown, which needs to be cleared. | ||||
278 | // TODO(nareshmodi): ensure that this is actually a TensorShape. | ||||
279 | PyErr_Clear(); | ||||
280 | } | ||||
281 | return size; | ||||
282 | } | ||||
283 | |||||
284 | bool IsInteger(PyObject* py_value) { | ||||
285 | #if PY_MAJOR_VERSION3 >= 3 | ||||
286 | return PyLong_Check(py_value)((((((PyObject*)(py_value))->ob_type))->tp_flags & ( (1UL << 24))) != 0); | ||||
287 | #else | ||||
288 | return PyInt_Check(py_value) || PyLong_Check(py_value)((((((PyObject*)(py_value))->ob_type))->tp_flags & ( (1UL << 24))) != 0); | ||||
289 | #endif | ||||
290 | } | ||||
291 | |||||
292 | // This function considers a Dimension._value of None to be valid, and sets the | ||||
293 | // value to be -1 in that case. | ||||
294 | bool ParseDimensionValue(const string& key, PyObject* py_value, | ||||
295 | TF_Status* status, int64_t* value) { | ||||
296 | if (IsInteger(py_value)) { | ||||
297 | return ParseInt64Value(key, py_value, status, value); | ||||
298 | } | ||||
299 | |||||
300 | tensorflow::Safe_PyObjectPtr dimension_value( | ||||
301 | PyObject_GetAttrString(py_value, "_value")); | ||||
302 | if (dimension_value == nullptr) { | ||||
303 | PyErr_Clear(); | ||||
304 | TF_SetStatus( | ||||
305 | status, TF_INVALID_ARGUMENT, | ||||
306 | tensorflow::strings::StrCat("Expecting a Dimension for attr ", key, | ||||
307 | ", got ", py_value->ob_type->tp_name) | ||||
308 | .c_str()); | ||||
309 | return false; | ||||
310 | } | ||||
311 | |||||
312 | if (dimension_value.get() == Py_None(&_Py_NoneStruct)) { | ||||
313 | *value = -1; | ||||
314 | return true; | ||||
315 | } | ||||
316 | |||||
317 | return ParseInt64Value(key, dimension_value.get(), status, value); | ||||
318 | } | ||||
319 | |||||
320 | bool ParseStringValue(const string& key, PyObject* py_value, TF_Status* status, | ||||
321 | tensorflow::StringPiece* value) { | ||||
322 | if (PyBytes_Check(py_value)((((((PyObject*)(py_value))->ob_type))->tp_flags & ( (1UL << 27))) != 0)) { | ||||
323 | Py_ssize_t size = 0; | ||||
324 | char* buf = nullptr; | ||||
325 | if (PyBytes_AsStringAndSize(py_value, &buf, &size) < 0) return false; | ||||
326 | *value = tensorflow::StringPiece(buf, size); | ||||
327 | return true; | ||||
328 | } | ||||
329 | #if PY_MAJOR_VERSION3 >= 3 | ||||
330 | if (PyUnicode_Check(py_value)((((((PyObject*)(py_value))->ob_type))->tp_flags & ( (1UL << 28))) != 0)) { | ||||
331 | Py_ssize_t size = 0; | ||||
332 | const char* buf = PyUnicode_AsUTF8AndSize(py_value, &size); | ||||
333 | if (buf == nullptr) return false; | ||||
334 | *value = tensorflow::StringPiece(buf, size); | ||||
335 | return true; | ||||
336 | } | ||||
337 | #endif | ||||
338 | TF_SetStatus( | ||||
339 | status, TF_INVALID_ARGUMENT, | ||||
340 | tensorflow::strings::StrCat("Expecting a string value for attr ", key, | ||||
341 | ", got ", py_value->ob_type->tp_name) | ||||
342 | .c_str()); | ||||
343 | return false; | ||||
344 | } | ||||
345 | |||||
346 | bool ParseBoolValue(const string& key, PyObject* py_value, TF_Status* status, | ||||
347 | unsigned char* value) { | ||||
348 | *value = PyObject_IsTrue(py_value); | ||||
349 | return true; | ||||
350 | } | ||||
351 | |||||
352 | // The passed in py_value is expected to be an object of the python type | ||||
353 | // dtypes.DType or an int. | ||||
354 | bool ParseTypeValue(const string& key, PyObject* py_value, TF_Status* status, | ||||
355 | int* value) { | ||||
356 | if (IsInteger(py_value)) { | ||||
357 | return ParseIntValue(key, py_value, status, value); | ||||
358 | } | ||||
359 | |||||
360 | tensorflow::Safe_PyObjectPtr py_type_enum( | ||||
361 | PyObject_GetAttrString(py_value, "_type_enum")); | ||||
362 | if (py_type_enum == nullptr) { | ||||
363 | PyErr_Clear(); | ||||
364 | TF_SetStatus( | ||||
365 | status, TF_INVALID_ARGUMENT, | ||||
366 | tensorflow::strings::StrCat("Expecting a DType.dtype for attr ", key, | ||||
367 | ", got ", py_value->ob_type->tp_name) | ||||
368 | .c_str()); | ||||
369 | return false; | ||||
370 | } | ||||
371 | |||||
372 | return ParseIntValue(key, py_type_enum.get(), status, value); | ||||
373 | } | ||||
374 | |||||
375 | bool SetOpAttrList(TFE_Context* ctx, TFE_Op* op, const char* key, | ||||
376 | PyObject* py_list, TF_AttrType type, | ||||
377 | tensorflow::gtl::FlatMap<string, int64_t>* attr_list_sizes, | ||||
378 | TF_Status* status) { | ||||
379 | if (!PySequence_Check(py_list)) { | ||||
380 | TF_SetStatus( | ||||
381 | status, TF_INVALID_ARGUMENT, | ||||
382 | tensorflow::strings::StrCat("Expecting sequence value for attr ", key, | ||||
383 | ", got ", py_list->ob_type->tp_name) | ||||
384 | .c_str()); | ||||
385 | return false; | ||||
386 | } | ||||
387 | const int num_values = PySequence_Size(py_list); | ||||
388 | if (attr_list_sizes != nullptr) (*attr_list_sizes)[key] = num_values; | ||||
389 | |||||
390 | #define PARSE_LIST(c_type, parse_fn) \ | ||||
391 | std::unique_ptr<c_type[]> values(new c_type[num_values]); \ | ||||
392 | for (int i = 0; i < num_values; ++i) { \ | ||||
393 | tensorflow::Safe_PyObjectPtr py_value(PySequence_ITEM(py_list, i)( (((PyObject*)(py_list))->ob_type)->tp_as_sequence-> sq_item(py_list, i) )); \ | ||||
394 | if (!parse_fn(key, py_value.get(), status, &values[i])) return false; \ | ||||
395 | } | ||||
396 | |||||
397 | if (type == TF_ATTR_STRING) { | ||||
398 | std::unique_ptr<const void*[]> values(new const void*[num_values]); | ||||
399 | std::unique_ptr<size_t[]> lengths(new size_t[num_values]); | ||||
400 | for (int i = 0; i < num_values; ++i) { | ||||
401 | tensorflow::StringPiece value; | ||||
402 | tensorflow::Safe_PyObjectPtr py_value(PySequence_ITEM(py_list, i)( (((PyObject*)(py_list))->ob_type)->tp_as_sequence-> sq_item(py_list, i) )); | ||||
403 | if (!ParseStringValue(key, py_value.get(), status, &value)) return false; | ||||
404 | values[i] = value.data(); | ||||
405 | lengths[i] = value.size(); | ||||
406 | } | ||||
407 | TFE_OpSetAttrStringList(op, key, values.get(), lengths.get(), num_values); | ||||
408 | } else if (type == TF_ATTR_INT) { | ||||
409 | PARSE_LIST(int64_t, ParseInt64Value); | ||||
410 | TFE_OpSetAttrIntList(op, key, values.get(), num_values); | ||||
411 | } else if (type == TF_ATTR_FLOAT) { | ||||
412 | PARSE_LIST(float, ParseFloatValue); | ||||
413 | TFE_OpSetAttrFloatList(op, key, values.get(), num_values); | ||||
414 | } else if (type == TF_ATTR_BOOL) { | ||||
415 | PARSE_LIST(unsigned char, ParseBoolValue); | ||||
416 | TFE_OpSetAttrBoolList(op, key, values.get(), num_values); | ||||
417 | } else if (type == TF_ATTR_TYPE) { | ||||
418 | PARSE_LIST(int, ParseTypeValue); | ||||
419 | TFE_OpSetAttrTypeList(op, key, | ||||
420 | reinterpret_cast<const TF_DataType*>(values.get()), | ||||
421 | num_values); | ||||
422 | } else if (type == TF_ATTR_SHAPE) { | ||||
423 | // Make one pass through the input counting the total number of | ||||
424 | // dims across all the input lists. | ||||
425 | int total_dims = 0; | ||||
426 | for (int i = 0; i < num_values; ++i) { | ||||
427 | tensorflow::Safe_PyObjectPtr py_value(PySequence_ITEM(py_list, i)( (((PyObject*)(py_list))->ob_type)->tp_as_sequence-> sq_item(py_list, i) )); | ||||
428 | if (py_value.get() != Py_None(&_Py_NoneStruct)) { | ||||
429 | if (!PySequence_Check(py_value.get())) { | ||||
430 | TF_SetStatus( | ||||
431 | status, TF_INVALID_ARGUMENT, | ||||
432 | tensorflow::strings::StrCat( | ||||
433 | "Expecting None or sequence value for element", i, | ||||
434 | " of attr ", key, ", got ", py_value->ob_type->tp_name) | ||||
435 | .c_str()); | ||||
436 | return false; | ||||
437 | } | ||||
438 | const auto size = TensorShapeNumDims(py_value.get()); | ||||
439 | if (size >= 0) { | ||||
440 | total_dims += size; | ||||
441 | } | ||||
442 | } | ||||
443 | } | ||||
444 | // Allocate a buffer that can fit all of the dims together. | ||||
445 | std::unique_ptr<int64_t[]> buffer(new int64_t[total_dims]); | ||||
446 | // Copy the input dims into the buffer and set dims to point to | ||||
447 | // the start of each list's dims. | ||||
448 | std::unique_ptr<const int64_t*[]> dims(new const int64_t*[num_values]); | ||||
449 | std::unique_ptr<int[]> num_dims(new int[num_values]); | ||||
450 | int64_t* offset = buffer.get(); | ||||
451 | for (int i = 0; i < num_values; ++i) { | ||||
452 | tensorflow::Safe_PyObjectPtr py_value(PySequence_ITEM(py_list, i)( (((PyObject*)(py_list))->ob_type)->tp_as_sequence-> sq_item(py_list, i) )); | ||||
453 | if (py_value.get() == Py_None(&_Py_NoneStruct)) { | ||||
454 | dims[i] = nullptr; | ||||
455 | num_dims[i] = -1; | ||||
456 | } else { | ||||
457 | const auto size = TensorShapeNumDims(py_value.get()); | ||||
458 | if (size == -1) { | ||||
459 | dims[i] = nullptr; | ||||
460 | num_dims[i] = -1; | ||||
461 | continue; | ||||
462 | } | ||||
463 | dims[i] = offset; | ||||
464 | num_dims[i] = size; | ||||
465 | for (int j = 0; j < size; ++j) { | ||||
466 | tensorflow::Safe_PyObjectPtr inner_py_value( | ||||
467 | PySequence_ITEM(py_value.get(), j)( (((PyObject*)(py_value.get()))->ob_type)->tp_as_sequence ->sq_item(py_value.get(), j) )); | ||||
468 | if (inner_py_value.get() == Py_None(&_Py_NoneStruct)) { | ||||
469 | *offset = -1; | ||||
470 | } else if (!ParseDimensionValue(key, inner_py_value.get(), status, | ||||
471 | offset)) { | ||||
472 | return false; | ||||
473 | } | ||||
474 | ++offset; | ||||
475 | } | ||||
476 | } | ||||
477 | } | ||||
478 | TFE_OpSetAttrShapeList(op, key, dims.get(), num_dims.get(), num_values, | ||||
479 | status); | ||||
480 | if (!status->status.ok()) return false; | ||||
481 | } else if (type == TF_ATTR_FUNC) { | ||||
482 | std::unique_ptr<const TFE_Op*[]> funcs(new const TFE_Op*[num_values]); | ||||
483 | for (int i = 0; i < num_values; ++i) { | ||||
484 | tensorflow::Safe_PyObjectPtr py_value(PySequence_ITEM(py_list, i)( (((PyObject*)(py_list))->ob_type)->tp_as_sequence-> sq_item(py_list, i) )); | ||||
485 | // Allow: | ||||
486 | // (1) String function name, OR | ||||
487 | // (2) A Python object with a .name attribute | ||||
488 | // (A crude test for being a | ||||
489 | // tensorflow.python.framework.function._DefinedFunction) | ||||
490 | // (which is what the various "defun" or "Defun" decorators do). | ||||
491 | // And in the future also allow an object that can encapsulate | ||||
492 | // the function name and its attribute values. | ||||
493 | tensorflow::StringPiece func_name; | ||||
494 | if (!ParseStringValue(key, py_value.get(), status, &func_name)) { | ||||
495 | PyObject* name_attr = PyObject_GetAttrString(py_value.get(), "name"); | ||||
| |||||
496 | if (name_attr == nullptr || | ||||
497 | !ParseStringValue(key, name_attr, status, &func_name)) { | ||||
498 | TF_SetStatus( | ||||
499 | status, TF_INVALID_ARGUMENT, | ||||
500 | tensorflow::strings::StrCat( | ||||
501 | "unable to set function value attribute from a ", | ||||
502 | py_value.get()->ob_type->tp_name, | ||||
503 | " object. If you think this is an error, please file an " | ||||
504 | "issue at " | ||||
505 | "https://github.com/tensorflow/tensorflow/issues/new") | ||||
506 | .c_str()); | ||||
507 | return false; | ||||
508 | } | ||||
509 | } | ||||
510 | funcs[i] = TFE_NewOp(ctx, func_name.data(), status); | ||||
511 | if (!status->status.ok()) return false; | ||||
512 | } | ||||
513 | TFE_OpSetAttrFunctionList(op, key, funcs.get(), num_values); | ||||
514 | if (!status->status.ok()) return false; | ||||
515 | } else { | ||||
516 | TF_SetStatus(status, TF_UNIMPLEMENTED, | ||||
517 | tensorflow::strings::StrCat("Attr ", key, | ||||
518 | " has unhandled list type ", type) | ||||
519 | .c_str()); | ||||
520 | return false; | ||||
521 | } | ||||
522 | #undef PARSE_LIST | ||||
523 | return true; | ||||
524 | } | ||||
525 | |||||
526 | TFE_Op* GetFunc(TFE_Context* ctx, const tensorflow::NameAttrList& func, | ||||
527 | TF_Status* status) { | ||||
528 | TFE_Op* func_op = TFE_NewOp(ctx, func.name().data(), status); | ||||
529 | for (const auto& attr : func.attr()) { | ||||
530 | if (!status->status.ok()) return nullptr; | ||||
531 | SetOpAttrValueScalar(ctx, func_op, attr.second, attr.first.data(), status); | ||||
532 | if (!status->status.ok()) return nullptr; | ||||
533 | } | ||||
534 | return func_op; | ||||
535 | } | ||||
536 | |||||
537 | void SetOpAttrListDefault( | ||||
538 | TFE_Context* ctx, TFE_Op* op, const tensorflow::OpDef::AttrDef& attr, | ||||
539 | const char* key, TF_AttrType type, | ||||
540 | tensorflow::gtl::FlatMap<string, int64_t>* attr_list_sizes, | ||||
541 | TF_Status* status) { | ||||
542 | if (type == TF_ATTR_STRING) { | ||||
543 | int num_values = attr.default_value().list().s_size(); | ||||
544 | std::unique_ptr<const void*[]> values(new const void*[num_values]); | ||||
545 | std::unique_ptr<size_t[]> lengths(new size_t[num_values]); | ||||
546 | (*attr_list_sizes)[key] = num_values; | ||||
547 | for (int i = 0; i < num_values; i++) { | ||||
548 | const string& v = attr.default_value().list().s(i); | ||||
549 | values[i] = v.data(); | ||||
550 | lengths[i] = v.size(); | ||||
551 | } | ||||
552 | TFE_OpSetAttrStringList(op, key, values.get(), lengths.get(), num_values); | ||||
553 | } else if (type == TF_ATTR_INT) { | ||||
554 | int num_values = attr.default_value().list().i_size(); | ||||
555 | std::unique_ptr<int64_t[]> values(new int64_t[num_values]); | ||||
556 | (*attr_list_sizes)[key] = num_values; | ||||
557 | for (int i = 0; i < num_values; i++) { | ||||
558 | values[i] = attr.default_value().list().i(i); | ||||
559 | } | ||||
560 | TFE_OpSetAttrIntList(op, key, values.get(), num_values); | ||||
561 | } else if (type == TF_ATTR_FLOAT) { | ||||
562 | int num_values = attr.default_value().list().f_size(); | ||||
563 | std::unique_ptr<float[]> values(new float[num_values]); | ||||
564 | (*attr_list_sizes)[key] = num_values; | ||||
565 | for (int i = 0; i < num_values; i++) { | ||||
566 | values[i] = attr.default_value().list().f(i); | ||||
567 | } | ||||
568 | TFE_OpSetAttrFloatList(op, key, values.get(), num_values); | ||||
569 | } else if (type == TF_ATTR_BOOL) { | ||||
570 | int num_values = attr.default_value().list().b_size(); | ||||
571 | std::unique_ptr<unsigned char[]> values(new unsigned char[num_values]); | ||||
572 | (*attr_list_sizes)[key] = num_values; | ||||
573 | for (int i = 0; i < num_values; i++) { | ||||
574 | values[i] = attr.default_value().list().b(i); | ||||
575 | } | ||||
576 | TFE_OpSetAttrBoolList(op, key, values.get(), num_values); | ||||
577 | } else if (type == TF_ATTR_TYPE) { | ||||
578 | int num_values = attr.default_value().list().type_size(); | ||||
579 | std::unique_ptr<int[]> values(new int[num_values]); | ||||
580 | (*attr_list_sizes)[key] = num_values; | ||||
581 | for (int i = 0; i < num_values; i++) { | ||||
582 | values[i] = attr.default_value().list().type(i); | ||||
583 | } | ||||
584 | TFE_OpSetAttrTypeList(op, key, | ||||
585 | reinterpret_cast<const TF_DataType*>(values.get()), | ||||
586 | attr.default_value().list().type_size()); | ||||
587 | } else if (type == TF_ATTR_SHAPE) { | ||||
588 | int num_values = attr.default_value().list().shape_size(); | ||||
589 | (*attr_list_sizes)[key] = num_values; | ||||
590 | int total_dims = 0; | ||||
591 | for (int i = 0; i < num_values; ++i) { | ||||
592 | if (!attr.default_value().list().shape(i).unknown_rank()) { | ||||
593 | total_dims += attr.default_value().list().shape(i).dim_size(); | ||||
594 | } | ||||
595 | } | ||||
596 | // Allocate a buffer that can fit all of the dims together. | ||||
597 | std::unique_ptr<int64_t[]> buffer(new int64_t[total_dims]); | ||||
598 | // Copy the input dims into the buffer and set dims to point to | ||||
599 | // the start of each list's dims. | ||||
600 | std::unique_ptr<const int64_t*[]> dims(new const int64_t*[num_values]); | ||||
601 | std::unique_ptr<int[]> num_dims(new int[num_values]); | ||||
602 | int64_t* offset = buffer.get(); | ||||
603 | for (int i = 0; i < num_values; ++i) { | ||||
604 | const auto& shape = attr.default_value().list().shape(i); | ||||
605 | if (shape.unknown_rank()) { | ||||
606 | dims[i] = nullptr; | ||||
607 | num_dims[i] = -1; | ||||
608 | } else { | ||||
609 | for (int j = 0; j < shape.dim_size(); j++) { | ||||
610 | *offset = shape.dim(j).size(); | ||||
611 | ++offset; | ||||
612 | } | ||||
613 | } | ||||
614 | } | ||||
615 | TFE_OpSetAttrShapeList(op, key, dims.get(), num_dims.get(), num_values, | ||||
616 | status); | ||||
617 | } else if (type == TF_ATTR_FUNC) { | ||||
618 | int num_values = attr.default_value().list().func_size(); | ||||
619 | (*attr_list_sizes)[key] = num_values; | ||||
620 | std::unique_ptr<const TFE_Op*[]> funcs(new const TFE_Op*[num_values]); | ||||
621 | for (int i = 0; i < num_values; i++) { | ||||
622 | funcs[i] = GetFunc(ctx, attr.default_value().list().func(i), status); | ||||
623 | } | ||||
624 | TFE_OpSetAttrFunctionList(op, key, funcs.get(), num_values); | ||||
625 | } else { | ||||
626 | TF_SetStatus(status, TF_UNIMPLEMENTED, | ||||
627 | "Lists of tensors are not yet implemented for default valued " | ||||
628 | "attributes for an operation."); | ||||
629 | } | ||||
630 | } | ||||
631 | |||||
632 | bool SetOpAttrScalar(TFE_Context* ctx, TFE_Op* op, const char* key, | ||||
633 | PyObject* py_value, TF_AttrType type, | ||||
634 | tensorflow::gtl::FlatMap<string, int64_t>* attr_list_sizes, | ||||
635 | TF_Status* status) { | ||||
636 | if (type == TF_ATTR_STRING) { | ||||
637 | tensorflow::StringPiece value; | ||||
638 | if (!ParseStringValue(key, py_value, status, &value)) return false; | ||||
639 | TFE_OpSetAttrString(op, key, value.data(), value.size()); | ||||
640 | } else if (type == TF_ATTR_INT) { | ||||
641 | int64_t value; | ||||
642 | if (!ParseInt64Value(key, py_value, status, &value)) return false; | ||||
643 | TFE_OpSetAttrInt(op, key, value); | ||||
644 | // attr_list_sizes is set for all int attributes (since at this point we are | ||||
645 | // not aware if that attribute might be used to calculate the size of an | ||||
646 | // output list or not). | ||||
647 | if (attr_list_sizes != nullptr) (*attr_list_sizes)[key] = value; | ||||
648 | } else if (type == TF_ATTR_FLOAT) { | ||||
649 | float value; | ||||
650 | if (!ParseFloatValue(key, py_value, status, &value)) return false; | ||||
651 | TFE_OpSetAttrFloat(op, key, value); | ||||
652 | } else if (type == TF_ATTR_BOOL) { | ||||
653 | unsigned char value; | ||||
654 | if (!ParseBoolValue(key, py_value, status, &value)) return false; | ||||
655 | TFE_OpSetAttrBool(op, key, value); | ||||
656 | } else if (type == TF_ATTR_TYPE) { | ||||
657 | int value; | ||||
658 | if (!ParseTypeValue(key, py_value, status, &value)) return false; | ||||
659 | TFE_OpSetAttrType(op, key, static_cast<TF_DataType>(value)); | ||||
660 | } else if (type == TF_ATTR_SHAPE) { | ||||
661 | if (py_value == Py_None(&_Py_NoneStruct)) { | ||||
662 | TFE_OpSetAttrShape(op, key, nullptr, -1, status); | ||||
663 | } else { | ||||
664 | if (!PySequence_Check(py_value)) { | ||||
665 | TF_SetStatus(status, TF_INVALID_ARGUMENT, | ||||
666 | tensorflow::strings::StrCat( | ||||
667 | "Expecting None or sequence value for attr", key, | ||||
668 | ", got ", py_value->ob_type->tp_name) | ||||
669 | .c_str()); | ||||
670 | return false; | ||||
671 | } | ||||
672 | const auto num_dims = TensorShapeNumDims(py_value); | ||||
673 | if (num_dims == -1) { | ||||
674 | TFE_OpSetAttrShape(op, key, nullptr, -1, status); | ||||
675 | return true; | ||||
676 | } | ||||
677 | std::unique_ptr<int64_t[]> dims(new int64_t[num_dims]); | ||||
678 | for (int i = 0; i < num_dims; ++i) { | ||||
679 | tensorflow::Safe_PyObjectPtr inner_py_value( | ||||
680 | PySequence_ITEM(py_value, i)( (((PyObject*)(py_value))->ob_type)->tp_as_sequence-> sq_item(py_value, i) )); | ||||
681 | if (inner_py_value.get() == Py_None(&_Py_NoneStruct)) { | ||||
682 | dims[i] = -1; | ||||
683 | } else if (!ParseDimensionValue(key, inner_py_value.get(), status, | ||||
684 | &dims[i])) { | ||||
685 | return false; | ||||
686 | } | ||||
687 | } | ||||
688 | TFE_OpSetAttrShape(op, key, dims.get(), num_dims, status); | ||||
689 | } | ||||
690 | if (!status->status.ok()) return false; | ||||
691 | } else if (type == TF_ATTR_FUNC) { | ||||
692 | // Allow: | ||||
693 | // (1) String function name, OR | ||||
694 | // (2) A Python object with a .name attribute | ||||
695 | // (A crude test for being a | ||||
696 | // tensorflow.python.framework.function._DefinedFunction) | ||||
697 | // (which is what the various "defun" or "Defun" decorators do). | ||||
698 | // And in the future also allow an object that can encapsulate | ||||
699 | // the function name and its attribute values. | ||||
700 | tensorflow::StringPiece func_name; | ||||
701 | if (!ParseStringValue(key, py_value, status, &func_name)) { | ||||
702 | PyObject* name_attr = PyObject_GetAttrString(py_value, "name"); | ||||
703 | if (name_attr == nullptr || | ||||
704 | !ParseStringValue(key, name_attr, status, &func_name)) { | ||||
705 | TF_SetStatus( | ||||
706 | status, TF_INVALID_ARGUMENT, | ||||
707 | tensorflow::strings::StrCat( | ||||
708 | "unable to set function value attribute from a ", | ||||
709 | py_value->ob_type->tp_name, | ||||
710 | " object. If you think this is an error, please file an issue " | ||||
711 | "at https://github.com/tensorflow/tensorflow/issues/new") | ||||
712 | .c_str()); | ||||
713 | return false; | ||||
714 | } | ||||
715 | } | ||||
716 | TF_SetStatus(status, TF_OK, ""); | ||||
717 | TFE_OpSetAttrFunctionName(op, key, func_name.data(), func_name.size()); | ||||
718 | } else { | ||||
719 | TF_SetStatus( | ||||
720 | status, TF_UNIMPLEMENTED, | ||||
721 | tensorflow::strings::StrCat("Attr ", key, " has unhandled type ", type) | ||||
722 | .c_str()); | ||||
723 | return false; | ||||
724 | } | ||||
725 | return true; | ||||
726 | } | ||||
727 | |||||
728 | void SetOpAttrScalarDefault( | ||||
729 | TFE_Context* ctx, TFE_Op* op, const tensorflow::AttrValue& default_value, | ||||
730 | const char* attr_name, | ||||
731 | tensorflow::gtl::FlatMap<string, int64_t>* attr_list_sizes, | ||||
732 | TF_Status* status) { | ||||
733 | SetOpAttrValueScalar(ctx, op, default_value, attr_name, status); | ||||
734 | if (default_value.value_case() == tensorflow::AttrValue::kI) { | ||||
735 | (*attr_list_sizes)[attr_name] = default_value.i(); | ||||
736 | } | ||||
737 | } | ||||
738 | |||||
739 | // start_index is the index at which the Tuple/List attrs will start getting | ||||
740 | // processed. | ||||
741 | void SetOpAttrs(TFE_Context* ctx, TFE_Op* op, PyObject* attrs, int start_index, | ||||
742 | TF_Status* out_status) { | ||||
743 | if (attrs == Py_None(&_Py_NoneStruct)) return; | ||||
744 | Py_ssize_t len = PyTuple_GET_SIZE(attrs)(((PyVarObject*)(((static_cast<void> (0)), (PyTupleObject *)(attrs))))->ob_size) - start_index; | ||||
745 | if ((len & 1) != 0) { | ||||
746 | TF_SetStatus(out_status, TF_INVALID_ARGUMENT, | ||||
747 | "Expecting attrs tuple to have even length."); | ||||
748 | return; | ||||
749 | } | ||||
750 | // Parse attrs | ||||
751 | for (Py_ssize_t i = 0; i < len; i += 2) { | ||||
752 | PyObject* py_key = PyTuple_GET_ITEM(attrs, start_index + i)(((static_cast<void> (0)), (PyTupleObject *)(attrs))-> ob_item[start_index + i]); | ||||
753 | PyObject* py_value = PyTuple_GET_ITEM(attrs, start_index + i + 1)(((static_cast<void> (0)), (PyTupleObject *)(attrs))-> ob_item[start_index + i + 1]); | ||||
754 | #if PY_MAJOR_VERSION3 >= 3 | ||||
755 | const char* key = PyBytes_Check(py_key)((((((PyObject*)(py_key))->ob_type))->tp_flags & (( 1UL << 27))) != 0) ? PyBytes_AsString(py_key) | ||||
756 | : PyUnicode_AsUTF8(py_key); | ||||
757 | #else | ||||
758 | const char* key = PyBytes_AsString(py_key); | ||||
759 | #endif | ||||
760 | unsigned char is_list = 0; | ||||
761 | const TF_AttrType type = TFE_OpGetAttrType(op, key, &is_list, out_status); | ||||
762 | if (!out_status->status.ok()) return; | ||||
763 | if (is_list != 0) { | ||||
764 | if (!SetOpAttrList(ctx, op, key, py_value, type, nullptr, out_status)) | ||||
765 | return; | ||||
766 | } else { | ||||
767 | if (!SetOpAttrScalar(ctx, op, key, py_value, type, nullptr, out_status)) | ||||
768 | return; | ||||
769 | } | ||||
770 | } | ||||
771 | } | ||||
772 | |||||
773 | // This function will set the op attrs required. If an attr has the value of | ||||
774 | // None, then it will read the AttrDef to get the default value and set that | ||||
775 | // instead. Any failure in this function will simply fall back to the slow | ||||
776 | // path. | ||||
777 | void SetOpAttrWithDefaults( | ||||
778 | TFE_Context* ctx, TFE_Op* op, const tensorflow::OpDef::AttrDef& attr, | ||||
779 | const char* attr_name, PyObject* attr_value, | ||||
780 | tensorflow::gtl::FlatMap<string, int64_t>* attr_list_sizes, | ||||
781 | TF_Status* status) { | ||||
782 | unsigned char is_list = 0; | ||||
783 | const TF_AttrType type = TFE_OpGetAttrType(op, attr_name, &is_list, status); | ||||
784 | if (!status->status.ok()) return; | ||||
785 | if (attr_value == Py_None(&_Py_NoneStruct)) { | ||||
786 | if (is_list != 0) { | ||||
787 | SetOpAttrListDefault(ctx, op, attr, attr_name, type, attr_list_sizes, | ||||
788 | status); | ||||
789 | } else { | ||||
790 | SetOpAttrScalarDefault(ctx, op, attr.default_value(), attr_name, | ||||
791 | attr_list_sizes, status); | ||||
792 | } | ||||
793 | } else { | ||||
794 | if (is_list != 0) { | ||||
795 | SetOpAttrList(ctx, op, attr_name, attr_value, type, attr_list_sizes, | ||||
796 | status); | ||||
797 | } else { | ||||
798 | SetOpAttrScalar(ctx, op, attr_name, attr_value, type, attr_list_sizes, | ||||
799 | status); | ||||
800 | } | ||||
801 | } | ||||
802 | } | ||||
803 | |||||
804 | PyObject* GetPythonObjectFromInt(int num) { | ||||
805 | #if PY_MAJOR_VERSION3 >= 3 | ||||
806 | return PyLong_FromLong(num); | ||||
807 | #else | ||||
808 | return PyInt_FromLong(num); | ||||
809 | #endif | ||||
810 | } | ||||
811 | |||||
812 | // Python subclass of Exception that is created on not ok Status. | ||||
813 | tensorflow::mutex exception_class_mutex(tensorflow::LINKER_INITIALIZED); | ||||
814 | PyObject* exception_class TF_GUARDED_BY(exception_class_mutex)__attribute__((guarded_by(exception_class_mutex))) = nullptr; | ||||
815 | |||||
816 | // Python subclass of Exception that is created to signal fallback. | ||||
817 | PyObject* fallback_exception_class = nullptr; | ||||
818 | |||||
819 | // Python function that returns input gradients given output gradients. | ||||
820 | PyObject* gradient_function = nullptr; | ||||
821 | |||||
822 | // Python function that returns output gradients given input gradients. | ||||
823 | PyObject* forward_gradient_function = nullptr; | ||||
824 | |||||
825 | static std::atomic<int64_t> _uid; | ||||
826 | |||||
827 | } // namespace | ||||
828 | |||||
829 | TF_Status* GetStatus() { | ||||
830 | TF_Status* maybe_status = ReleaseThreadLocalStatus(); | ||||
831 | if (maybe_status) { | ||||
832 | TF_SetStatus(maybe_status, TF_OK, ""); | ||||
833 | return maybe_status; | ||||
834 | } else { | ||||
835 | return TF_NewStatus(); | ||||
836 | } | ||||
837 | } | ||||
838 | |||||
839 | void ReturnStatus(TF_Status* status) { | ||||
840 | TF_SetStatus(status, TF_OK, ""); | ||||
841 | thread_local_tf_status.reset(status); | ||||
842 | } | ||||
843 | |||||
844 | void TFE_Py_Execute(TFE_Context* ctx, const char* device_name, | ||||
845 | const char* op_name, TFE_InputTensorHandles* inputs, | ||||
846 | PyObject* attrs, TFE_OutputTensorHandles* outputs, | ||||
847 | TF_Status* out_status) { | ||||
848 | TFE_Py_ExecuteCancelable(ctx, device_name, op_name, inputs, attrs, | ||||
| |||||
849 | /*cancellation_manager=*/nullptr, outputs, | ||||
850 | out_status); | ||||
851 | } | ||||
852 | |||||
853 | void TFE_Py_ExecuteCancelable(TFE_Context* ctx, const char* device_name, | ||||
854 | const char* op_name, | ||||
855 | TFE_InputTensorHandles* inputs, PyObject* attrs, | ||||
856 | TFE_CancellationManager* cancellation_manager, | ||||
857 | TFE_OutputTensorHandles* outputs, | ||||
858 | TF_Status* out_status) { | ||||
859 | tensorflow::profiler::TraceMe activity( | ||||
860 | "TFE_Py_ExecuteCancelable", tensorflow::profiler::TraceMeLevel::kInfo); | ||||
861 | |||||
862 | TFE_Op* op = GetOp(ctx, op_name, device_name, out_status); | ||||
863 | |||||
864 | auto cleaner = tensorflow::gtl::MakeCleanup([ctx, op] { ReturnOp(ctx, op); }); | ||||
865 | if (!out_status->status.ok()) return; | ||||
866 | |||||
867 | tensorflow::unwrap(op)->SetStackTrace(tensorflow::GetStackTrace( | ||||
868 | tensorflow::StackTrace::kStackTraceInitialSize)); | ||||
869 | |||||
870 | for (int i = 0; i < inputs->size() && out_status->status.ok(); ++i) { | ||||
871 | TFE_OpAddInput(op, inputs->at(i), out_status); | ||||
872 | } | ||||
873 | if (cancellation_manager
| ||||
874 | TFE_OpSetCancellationManager(op, cancellation_manager, out_status); | ||||
875 | } | ||||
876 | if (out_status->status.ok()) { | ||||
877 | SetOpAttrs(ctx, op, attrs, 0, out_status); | ||||
878 | } | ||||
879 | Py_BEGIN_ALLOW_THREADS{ PyThreadState *_save; _save = PyEval_SaveThread();; | ||||
880 | |||||
881 | int num_outputs = outputs->size(); | ||||
882 | |||||
883 | if (out_status->status.ok()) { | ||||
884 | TFE_Execute(op, outputs->data(), &num_outputs, out_status); | ||||
885 | } | ||||
886 | |||||
887 | if (out_status->status.ok()) { | ||||
888 | outputs->resize(num_outputs); | ||||
889 | } else { | ||||
890 | TF_SetStatus(out_status, TF_GetCode(out_status), | ||||
891 | tensorflow::strings::StrCat(TF_Message(out_status), | ||||
892 | " [Op:", op_name, "]") | ||||
893 | .c_str()); | ||||
894 | } | ||||
895 | |||||
896 | Py_END_ALLOW_THREADSPyEval_RestoreThread(_save); }; | ||||
897 | } | ||||
898 | |||||
899 | PyObject* TFE_Py_RegisterExceptionClass(PyObject* e) { | ||||
900 | tensorflow::mutex_lock l(exception_class_mutex); | ||||
901 | if (exception_class != nullptr) { | ||||
902 | Py_DECREF(exception_class)_Py_DECREF(((PyObject*)(exception_class))); | ||||
903 | } | ||||
904 | if (PyObject_IsSubclass(e, PyExc_Exception) <= 0) { | ||||
905 | exception_class = nullptr; | ||||
906 | PyErr_SetString(PyExc_TypeError, | ||||
907 | "TFE_Py_RegisterExceptionClass: " | ||||
908 | "Registered class should be subclass of Exception."); | ||||
909 | return nullptr; | ||||
910 | } | ||||
911 | |||||
912 | Py_INCREF(e)_Py_INCREF(((PyObject*)(e))); | ||||
913 | exception_class = e; | ||||
914 | Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (& _Py_NoneStruct); | ||||
915 | } | ||||
916 | |||||
917 | PyObject* TFE_Py_RegisterFallbackExceptionClass(PyObject* e) { | ||||
918 | if (fallback_exception_class != nullptr) { | ||||
919 | Py_DECREF(fallback_exception_class)_Py_DECREF(((PyObject*)(fallback_exception_class))); | ||||
920 | } | ||||
921 | if (PyObject_IsSubclass(e, PyExc_Exception) <= 0) { | ||||
922 | fallback_exception_class = nullptr; | ||||
923 | PyErr_SetString(PyExc_TypeError, | ||||
924 | "TFE_Py_RegisterFallbackExceptionClass: " | ||||
925 | "Registered class should be subclass of Exception."); | ||||
926 | return nullptr; | ||||
927 | } else { | ||||
928 | Py_INCREF(e)_Py_INCREF(((PyObject*)(e))); | ||||
929 | fallback_exception_class = e; | ||||
930 | Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (& _Py_NoneStruct); | ||||
931 | } | ||||
932 | } | ||||
933 | |||||
934 | PyObject* TFE_Py_RegisterGradientFunction(PyObject* e) { | ||||
935 | if (gradient_function != nullptr) { | ||||
936 | Py_DECREF(gradient_function)_Py_DECREF(((PyObject*)(gradient_function))); | ||||
937 | } | ||||
938 | if (!PyCallable_Check(e)) { | ||||
939 | gradient_function = nullptr; | ||||
940 | PyErr_SetString(PyExc_TypeError, | ||||
941 | "TFE_Py_RegisterGradientFunction: " | ||||
942 | "Registered object should be function."); | ||||
943 | return nullptr; | ||||
944 | } else { | ||||
945 | Py_INCREF(e)_Py_INCREF(((PyObject*)(e))); | ||||
946 | gradient_function = e; | ||||
947 | Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (& _Py_NoneStruct); | ||||
948 | } | ||||
949 | } | ||||
950 | |||||
951 | PyObject* TFE_Py_RegisterJVPFunction(PyObject* e) { | ||||
952 | if (forward_gradient_function != nullptr) { | ||||
953 | Py_DECREF(forward_gradient_function)_Py_DECREF(((PyObject*)(forward_gradient_function))); | ||||
954 | } | ||||
955 | if (!PyCallable_Check(e)) { | ||||
956 | forward_gradient_function = nullptr; | ||||
957 | PyErr_SetString(PyExc_TypeError, | ||||
958 | "TFE_Py_RegisterJVPFunction: " | ||||
959 | "Registered object should be function."); | ||||
960 | return nullptr; | ||||
961 | } else { | ||||
962 | Py_INCREF(e)_Py_INCREF(((PyObject*)(e))); | ||||
963 | forward_gradient_function = e; | ||||
964 | Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (& _Py_NoneStruct); | ||||
965 | } | ||||
966 | } | ||||
967 | |||||
968 | void RaiseFallbackException(const char* message) { | ||||
969 | if (fallback_exception_class != nullptr) { | ||||
970 | PyErr_SetString(fallback_exception_class, message); | ||||
971 | return; | ||||
972 | } | ||||
973 | |||||
974 | PyErr_SetString( | ||||
975 | PyExc_RuntimeError, | ||||
976 | tensorflow::strings::StrCat( | ||||
977 | "Fallback exception type not set, attempting to fallback due to ", | ||||
978 | message) | ||||
979 | .data()); | ||||
980 | } | ||||
981 | |||||
982 | // Format and return `status`' error message with the attached stack trace if | ||||
983 | // available. `status` must have an error. | ||||
984 | std::string FormatErrorStatusStackTrace(const tensorflow::Status& status) { | ||||
985 | tensorflow::DCheckPyGilState(); | ||||
986 | DCHECK(!status.ok())while (false && (!status.ok())) ::tensorflow::internal ::LogMessageFatal("tensorflow/python/eager/pywrap_tfe_src.cc" , 986); | ||||
987 | |||||
988 | if (status.stack_trace().empty()) return status.error_message(); | ||||
989 | |||||
990 | const std::vector<tensorflow::StackFrame>& stack_trace = status.stack_trace(); | ||||
991 | |||||
992 | PyObject* linecache = PyImport_ImportModule("linecache"); | ||||
993 | PyObject* getline = | ||||
994 | PyObject_GetAttr(linecache, PyUnicode_FromString("getline")); | ||||
995 | DCHECK(getline)while (false && (getline)) ::tensorflow::internal::LogMessageFatal ("tensorflow/python/eager/pywrap_tfe_src.cc", 995); | ||||
996 | |||||
997 | std::ostringstream result; | ||||
998 | result << "Exception originated from\n\n"; | ||||
999 | |||||
1000 | for (const tensorflow::StackFrame& stack_frame : stack_trace) { | ||||
1001 | PyObject* line_str_obj = PyObject_CallFunction( | ||||
1002 | getline, const_cast<char*>("si"), stack_frame.file_name.c_str(), | ||||
1003 | stack_frame.line_number); | ||||
1004 | tensorflow::StringPiece line_str = TFE_GetPythonString(line_str_obj); | ||||
1005 | tensorflow::str_util::RemoveWhitespaceContext(&line_str); | ||||
1006 | result << " File \"" << stack_frame.file_name << "\", line " | ||||
1007 | << stack_frame.line_number << ", in " << stack_frame.function_name | ||||
1008 | << '\n'; | ||||
1009 | |||||
1010 | if (!line_str.empty()) result << " " << line_str << '\n'; | ||||
1011 | Py_XDECREF(line_str_obj)_Py_XDECREF(((PyObject*)(line_str_obj))); | ||||
1012 | } | ||||
1013 | |||||
1014 | Py_DecRef(getline); | ||||
1015 | Py_DecRef(linecache); | ||||
1016 | |||||
1017 | result << '\n' << status.error_message(); | ||||
1018 | return result.str(); | ||||
1019 | } | ||||
1020 | |||||
1021 | int MaybeRaiseExceptionFromTFStatus(TF_Status* status, PyObject* exception) { | ||||
1022 | if (status->status.ok()) return 0; | ||||
1023 | const char* msg = TF_Message(status); | ||||
1024 | if (exception == nullptr) { | ||||
1025 | tensorflow::mutex_lock l(exception_class_mutex); | ||||
1026 | if (exception_class != nullptr) { | ||||
1027 | tensorflow::Safe_PyObjectPtr payloads(PyDict_New()); | ||||
1028 | for (const auto& payload : | ||||
1029 | tensorflow::errors::GetPayloads(status->status)) { | ||||
1030 | PyDict_SetItem(payloads.get(), | ||||
1031 | PyBytes_FromString(payload.first.c_str()), | ||||
1032 | PyBytes_FromString(payload.second.c_str())); | ||||
1033 | } | ||||
1034 | tensorflow::Safe_PyObjectPtr val(Py_BuildValue( | ||||
1035 | "siO", FormatErrorStatusStackTrace(status->status).c_str(), | ||||
1036 | TF_GetCode(status), payloads.get())); | ||||
1037 | if (PyErr_Occurred()) { | ||||
1038 | // NOTE: This hides the actual error (i.e. the reason `status` was not | ||||
1039 | // TF_OK), but there is nothing we can do at this point since we can't | ||||
1040 | // generate a reasonable error from the status. | ||||
1041 | // Consider adding a message explaining this. | ||||
1042 | return -1; | ||||
1043 | } | ||||
1044 | PyErr_SetObject(exception_class, val.get()); | ||||
1045 | return -1; | ||||
1046 | } else { | ||||
1047 | exception = PyExc_RuntimeError; | ||||
1048 | } | ||||
1049 | } | ||||
1050 | // May be update already set exception. | ||||
1051 | PyErr_SetString(exception, msg); | ||||
1052 | return -1; | ||||
1053 | } | ||||
1054 | |||||
1055 | int MaybeRaiseExceptionFromStatus(const tensorflow::Status& status, | ||||
1056 | PyObject* exception) { | ||||
1057 | if (status.ok()) return 0; | ||||
1058 | const char* msg = status.error_message().c_str(); | ||||
1059 | if (exception == nullptr) { | ||||
1060 | tensorflow::mutex_lock l(exception_class_mutex); | ||||
1061 | if (exception_class != nullptr) { | ||||
1062 | tensorflow::Safe_PyObjectPtr payloads(PyDict_New()); | ||||
1063 | for (const auto& element : tensorflow::errors::GetPayloads(status)) { | ||||
1064 | PyDict_SetItem(payloads.get(), | ||||
1065 | PyBytes_FromString(element.first.c_str()), | ||||
1066 | PyBytes_FromString(element.second.c_str())); | ||||
1067 | } | ||||
1068 | tensorflow::Safe_PyObjectPtr val( | ||||
1069 | Py_BuildValue("siO", FormatErrorStatusStackTrace(status).c_str(), | ||||
1070 | status.code(), payloads.get())); | ||||
1071 | PyErr_SetObject(exception_class, val.get()); | ||||
1072 | return -1; | ||||
1073 | } else { | ||||
1074 | exception = PyExc_RuntimeError; | ||||
1075 | } | ||||
1076 | } | ||||
1077 | // May be update already set exception. | ||||
1078 | PyErr_SetString(exception, msg); | ||||
1079 | return -1; | ||||
1080 | } | ||||
1081 | |||||
1082 | const char* TFE_GetPythonString(PyObject* o) { | ||||
1083 | #if PY_MAJOR_VERSION3 >= 3 | ||||
1084 | if (PyBytes_Check(o)((((((PyObject*)(o))->ob_type))->tp_flags & ((1UL << 27))) != 0)) { | ||||
1085 | return PyBytes_AsString(o); | ||||
1086 | } else { | ||||
1087 | return PyUnicode_AsUTF8(o); | ||||
1088 | } | ||||
1089 | #else | ||||
1090 | return PyBytes_AsString(o); | ||||
1091 | #endif | ||||
1092 | } | ||||
1093 | |||||
1094 | int64_t get_uid() { return _uid++; } | ||||
1095 | |||||
1096 | PyObject* TFE_Py_UID() { return PyLong_FromLongLong(get_uid()); } | ||||
1097 | |||||
1098 | void TFE_DeleteContextCapsule(PyObject* context) { | ||||
1099 | TFE_Context* ctx = | ||||
1100 | reinterpret_cast<TFE_Context*>(PyCapsule_GetPointer(context, nullptr)); | ||||
1101 | auto op = ReleaseThreadLocalOp(ctx); | ||||
1102 | op.reset(); | ||||
1103 | TFE_DeleteContext(ctx); | ||||
1104 | } | ||||
1105 | |||||
1106 | static int64_t MakeInt(PyObject* integer) { | ||||
1107 | #if PY_MAJOR_VERSION3 >= 3 | ||||
1108 | return PyLong_AsLong(integer); | ||||
1109 | #else | ||||
1110 | return PyInt_AsLong(integer); | ||||
1111 | #endif | ||||
1112 | } | ||||
1113 | |||||
1114 | static int64_t FastTensorId(PyObject* tensor) { | ||||
1115 | if (EagerTensor_CheckExact(tensor)) { | ||||
1116 | return PyEagerTensor_ID(tensor); | ||||
1117 | } | ||||
1118 | PyObject* id_field = PyObject_GetAttrString(tensor, "_id"); | ||||
1119 | if (id_field == nullptr) { | ||||
1120 | return -1; | ||||
1121 | } | ||||
1122 | int64_t id = MakeInt(id_field); | ||||
1123 | Py_DECREF(id_field)_Py_DECREF(((PyObject*)(id_field))); | ||||
1124 | return id; | ||||
1125 | } | ||||
1126 | |||||
1127 | namespace tensorflow { | ||||
1128 | DataType PyTensor_DataType(PyObject* tensor) { | ||||
1129 | if (EagerTensor_CheckExact(tensor)) { | ||||
1130 | return PyEagerTensor_Dtype(tensor); | ||||
1131 | } else { | ||||
1132 | #if PY_MAJOR_VERSION3 < 3 | ||||
1133 | // Python 2.x: | ||||
1134 | static PyObject* dtype_attr = PyString_InternFromString("dtype"); | ||||
1135 | static PyObject* type_enum_attr = PyString_InternFromString("_type_enum"); | ||||
1136 | #else | ||||
1137 | // Python 3.x: | ||||
1138 | static PyObject* dtype_attr = PyUnicode_InternFromString("dtype"); | ||||
1139 | static PyObject* type_enum_attr = PyUnicode_InternFromString("_type_enum"); | ||||
1140 | #endif | ||||
1141 | Safe_PyObjectPtr dtype_field(PyObject_GetAttr(tensor, dtype_attr)); | ||||
1142 | if (!dtype_field) { | ||||
1143 | return DT_INVALID; | ||||
1144 | } | ||||
1145 | |||||
1146 | Safe_PyObjectPtr enum_field( | ||||
1147 | PyObject_GetAttr(dtype_field.get(), type_enum_attr)); | ||||
1148 | if (!enum_field) { | ||||
1149 | return DT_INVALID; | ||||
1150 | } | ||||
1151 | |||||
1152 | return static_cast<DataType>(MakeInt(enum_field.get())); | ||||
1153 | } | ||||
1154 | } | ||||
1155 | } // namespace tensorflow | ||||
1156 | |||||
1157 | class PyTapeTensor { | ||||
1158 | public: | ||||
1159 | PyTapeTensor(int64_t id, tensorflow::DataType dtype, | ||||
1160 | const tensorflow::TensorShape& shape) | ||||
1161 | : id_(id), dtype_(dtype), shape_(shape) {} | ||||
1162 | PyTapeTensor(int64_t id, tensorflow::DataType dtype, PyObject* shape) | ||||
1163 | : id_(id), dtype_(dtype), shape_(shape) { | ||||
1164 | Py_INCREF(absl::get<1>(shape_))_Py_INCREF(((PyObject*)(absl::get<1>(shape_)))); | ||||
1165 | } | ||||
1166 | PyTapeTensor(const PyTapeTensor& other) { | ||||
1167 | id_ = other.id_; | ||||
1168 | dtype_ = other.dtype_; | ||||
1169 | shape_ = other.shape_; | ||||
1170 | if (shape_.index() == 1) { | ||||
1171 | Py_INCREF(absl::get<1>(shape_))_Py_INCREF(((PyObject*)(absl::get<1>(shape_)))); | ||||
1172 | } | ||||
1173 | } | ||||
1174 | |||||
1175 | ~PyTapeTensor() { | ||||
1176 | if (shape_.index() == 1) { | ||||
1177 | Py_DECREF(absl::get<1>(shape_))_Py_DECREF(((PyObject*)(absl::get<1>(shape_)))); | ||||
1178 | } | ||||
1179 | } | ||||
1180 | PyObject* GetShape() const; | ||||
1181 | PyObject* GetPyDType() const { return PyLong_FromLong(dtype_); } | ||||
1182 | int64_t GetID() const { return id_; } | ||||
1183 | tensorflow::DataType GetDType() const { return dtype_; } | ||||
1184 | |||||
1185 | PyObject* OnesLike() const; | ||||
1186 | PyObject* ZerosLike() const; | ||||
1187 | |||||
1188 | private: | ||||
1189 | int64_t id_; | ||||
1190 | tensorflow::DataType dtype_; | ||||
1191 | |||||
1192 | // Note that if shape_.index() == 1, meaning shape_ contains a PyObject, that | ||||
1193 | // PyObject is the tensor itself. This is used to support tf.shape(tensor) for | ||||
1194 | // partially-defined shapes and tf.zeros_like(tensor) for variant-dtype | ||||
1195 | // tensors. | ||||
1196 | absl::variant<tensorflow::TensorShape, PyObject*> shape_; | ||||
1197 | }; | ||||
1198 | |||||
1199 | static PyTapeTensor TapeTensorFromTensor(PyObject* tensor); | ||||
1200 | |||||
1201 | class PyVSpace : public tensorflow::eager::VSpace<PyObject, PyBackwardFunction, | ||||
1202 | PyTapeTensor> { | ||||
1203 | public: | ||||
1204 | explicit PyVSpace(PyObject* py_vspace) : py_vspace_(py_vspace) { | ||||
1205 | Py_INCREF(py_vspace_)_Py_INCREF(((PyObject*)(py_vspace_))); | ||||
1206 | } | ||||
1207 | |||||
1208 | tensorflow::Status Initialize() { | ||||
1209 | num_elements_ = PyObject_GetAttrString(py_vspace_, "num_elements_fn"); | ||||
1210 | if (num_elements_ == nullptr) { | ||||
1211 | return tensorflow::errors::InvalidArgument("invalid vspace"); | ||||
1212 | } | ||||
1213 | aggregate_fn_ = PyObject_GetAttrString(py_vspace_, "aggregate_fn"); | ||||
1214 | if (aggregate_fn_ == nullptr) { | ||||
1215 | return tensorflow::errors::InvalidArgument("invalid vspace"); | ||||
1216 | } | ||||
1217 | zeros_fn_ = PyObject_GetAttrString(py_vspace_, "zeros_fn"); | ||||
1218 | if (zeros_fn_ == nullptr) { | ||||
1219 | return tensorflow::errors::InvalidArgument("invalid vspace"); | ||||
1220 | } | ||||
1221 | zeros_like_fn_ = PyObject_GetAttrString(py_vspace_, "zeros_like_fn"); | ||||
1222 | if (zeros_like_fn_ == nullptr) { | ||||
1223 | return tensorflow::errors::InvalidArgument("invalid vspace"); | ||||
1224 | } | ||||
1225 | ones_fn_ = PyObject_GetAttrString(py_vspace_, "ones_fn"); | ||||
1226 | if (ones_fn_ == nullptr) { | ||||
1227 | return tensorflow::errors::InvalidArgument("invalid vspace"); | ||||
1228 | } | ||||
1229 | ones_like_fn_ = PyObject_GetAttrString(py_vspace_, "ones_like_fn"); | ||||
1230 | if (ones_like_fn_ == nullptr) { | ||||
1231 | return tensorflow::errors::InvalidArgument("invalid vspace"); | ||||
1232 | } | ||||
1233 | graph_shape_fn_ = PyObject_GetAttrString(py_vspace_, "graph_shape_fn"); | ||||
1234 | if (graph_shape_fn_ == nullptr) { | ||||
1235 | return tensorflow::errors::InvalidArgument("invalid vspace"); | ||||
1236 | } | ||||
1237 | return tensorflow::Status::OK(); | ||||
1238 | } | ||||
1239 | |||||
1240 | ~PyVSpace() override { | ||||
1241 | Py_XDECREF(num_elements_)_Py_XDECREF(((PyObject*)(num_elements_))); | ||||
1242 | Py_XDECREF(aggregate_fn_)_Py_XDECREF(((PyObject*)(aggregate_fn_))); | ||||
1243 | Py_XDECREF(zeros_fn_)_Py_XDECREF(((PyObject*)(zeros_fn_))); | ||||
1244 | Py_XDECREF(zeros_like_fn_)_Py_XDECREF(((PyObject*)(zeros_like_fn_))); | ||||
1245 | Py_XDECREF(ones_fn_)_Py_XDECREF(((PyObject*)(ones_fn_))); | ||||
1246 | Py_XDECREF(ones_like_fn_)_Py_XDECREF(((PyObject*)(ones_like_fn_))); | ||||
1247 | Py_XDECREF(graph_shape_fn_)_Py_XDECREF(((PyObject*)(graph_shape_fn_))); | ||||
1248 | |||||
1249 | Py_DECREF(py_vspace_)_Py_DECREF(((PyObject*)(py_vspace_))); | ||||
1250 | } | ||||
1251 | |||||
1252 | int64_t NumElements(PyObject* tensor) const final { | ||||
1253 | if (EagerTensor_CheckExact(tensor)) { | ||||
1254 | return PyEagerTensor_NumElements(tensor); | ||||
1255 | } | ||||
1256 | PyObject* arglist = | ||||
1257 | Py_BuildValue("(O)", reinterpret_cast<PyObject*>(tensor)); | ||||
1258 | PyObject* result = PyEval_CallObject(num_elements_, arglist)PyEval_CallObjectWithKeywords(num_elements_, arglist, (PyObject *)__null); | ||||
1259 | Py_DECREF(arglist)_Py_DECREF(((PyObject*)(arglist))); | ||||
1260 | if (result == nullptr) { | ||||
1261 | // The caller detects whether a python exception has been raised. | ||||
1262 | return -1; | ||||
1263 | } | ||||
1264 | int64_t r = MakeInt(result); | ||||
1265 | Py_DECREF(result)_Py_DECREF(((PyObject*)(result))); | ||||
1266 | return r; | ||||
1267 | } | ||||
1268 | |||||
1269 | PyObject* AggregateGradients( | ||||
1270 | tensorflow::gtl::ArraySlice<PyObject*> gradient_tensors) const final { | ||||
1271 | PyObject* list = PyList_New(gradient_tensors.size()); | ||||
1272 | for (int i = 0; i < gradient_tensors.size(); ++i) { | ||||
1273 | // Note: stealing a reference to the gradient tensors. | ||||
1274 | CHECK(gradient_tensors[i] != nullptr)if ((__builtin_expect(!(gradient_tensors[i] != nullptr), 0))) ::tensorflow::internal::LogMessageFatal("tensorflow/python/eager/pywrap_tfe_src.cc" , 1274) << "Check failed: " "gradient_tensors[i] != nullptr" " "; | ||||
1275 | CHECK(gradient_tensors[i] != Py_None)if ((__builtin_expect(!(gradient_tensors[i] != (&_Py_NoneStruct )), 0))) ::tensorflow::internal::LogMessageFatal("tensorflow/python/eager/pywrap_tfe_src.cc" , 1275) << "Check failed: " "gradient_tensors[i] != Py_None" " "; | ||||
1276 | PyList_SET_ITEM(list, i,PyList_SetItem(list, i, reinterpret_cast<PyObject*>(gradient_tensors [i])) | ||||
1277 | reinterpret_cast<PyObject*>(gradient_tensors[i]))PyList_SetItem(list, i, reinterpret_cast<PyObject*>(gradient_tensors [i])); | ||||
1278 | } | ||||
1279 | PyObject* arglist = Py_BuildValue("(O)", list); | ||||
1280 | CHECK(arglist != nullptr)if ((__builtin_expect(!(arglist != nullptr), 0))) ::tensorflow ::internal::LogMessageFatal("tensorflow/python/eager/pywrap_tfe_src.cc" , 1280) << "Check failed: " "arglist != nullptr" " "; | ||||
1281 | PyObject* result = PyEval_CallObject(aggregate_fn_, arglist)PyEval_CallObjectWithKeywords(aggregate_fn_, arglist, (PyObject *)__null); | ||||
1282 | Py_DECREF(arglist)_Py_DECREF(((PyObject*)(arglist))); | ||||
1283 | Py_DECREF(list)_Py_DECREF(((PyObject*)(list))); | ||||
1284 | return result; | ||||
1285 | } | ||||
1286 | |||||
1287 | int64_t TensorId(PyObject* tensor) const final { | ||||
1288 | return FastTensorId(tensor); | ||||
1289 | } | ||||
1290 | |||||
1291 | void MarkAsResult(PyObject* gradient) const final { Py_INCREF(gradient)_Py_INCREF(((PyObject*)(gradient))); } | ||||
1292 | |||||
1293 | PyObject* Ones(PyObject* shape, PyObject* dtype) const { | ||||
1294 | if (PyErr_Occurred()) { | ||||
1295 | return nullptr; | ||||
1296 | } | ||||
1297 | PyObject* arg_list = Py_BuildValue("OO", shape, dtype); | ||||
1298 | PyObject* result = PyEval_CallObject(ones_fn_, arg_list)PyEval_CallObjectWithKeywords(ones_fn_, arg_list, (PyObject * )__null); | ||||
1299 | Py_DECREF(arg_list)_Py_DECREF(((PyObject*)(arg_list))); | ||||
1300 | return result; | ||||
1301 | } | ||||
1302 | |||||
1303 | PyObject* OnesLike(PyObject* tensor) const { | ||||
1304 | if (PyErr_Occurred()) { | ||||
1305 | return nullptr; | ||||
1306 | } | ||||
1307 | return PyObject_CallFunctionObjArgs(ones_like_fn_, tensor, NULL__null); | ||||
1308 | } | ||||
1309 | |||||
1310 | // Builds a tensor filled with ones with the same shape and dtype as `t`. | ||||
1311 | Status BuildOnesLike(const PyTapeTensor& t, | ||||
1312 | PyObject** result) const override { | ||||
1313 | *result = t.OnesLike(); | ||||
1314 | return Status::OK(); | ||||
1315 | } | ||||
1316 | |||||
1317 | PyObject* Zeros(PyObject* shape, PyObject* dtype) const { | ||||
1318 | if (PyErr_Occurred()) { | ||||
1319 | return nullptr; | ||||
1320 | } | ||||
1321 | PyObject* arg_list = Py_BuildValue("OO", shape, dtype); | ||||
1322 | PyObject* result = PyEval_CallObject(zeros_fn_, arg_list)PyEval_CallObjectWithKeywords(zeros_fn_, arg_list, (PyObject * )__null); | ||||
1323 | Py_DECREF(arg_list)_Py_DECREF(((PyObject*)(arg_list))); | ||||
1324 | return result; | ||||
1325 | } | ||||
1326 | |||||
1327 | PyObject* ZerosLike(PyObject* tensor) const { | ||||
1328 | if (PyErr_Occurred()) { | ||||
1329 | return nullptr; | ||||
1330 | } | ||||
1331 | return PyObject_CallFunctionObjArgs(zeros_like_fn_, tensor, NULL__null); | ||||
1332 | } | ||||
1333 | |||||
1334 | PyObject* GraphShape(PyObject* tensor) const { | ||||
1335 | PyObject* arg_list = Py_BuildValue("(O)", tensor); | ||||
1336 | PyObject* result = PyEval_CallObject(graph_shape_fn_, arg_list)PyEval_CallObjectWithKeywords(graph_shape_fn_, arg_list, (PyObject *)__null); | ||||
1337 | Py_DECREF(arg_list)_Py_DECREF(((PyObject*)(arg_list))); | ||||
1338 | return result; | ||||
1339 | } | ||||
1340 | |||||
1341 | tensorflow::Status CallBackwardFunction( | ||||
1342 | const string& op_type, PyBackwardFunction* backward_function, | ||||
1343 | const std::vector<int64_t>& unneeded_gradients, | ||||
1344 | tensorflow::gtl::ArraySlice<PyObject*> output_gradients, | ||||
1345 | absl::Span<PyObject*> result) const final { | ||||
1346 | PyObject* grads = PyTuple_New(output_gradients.size()); | ||||
1347 | for (int i = 0; i < output_gradients.size(); ++i) { | ||||
1348 | if (output_gradients[i] == nullptr) { | ||||
1349 | Py_INCREF(Py_None)_Py_INCREF(((PyObject*)((&_Py_NoneStruct)))); | ||||
1350 | PyTuple_SET_ITEM(grads, i, Py_None)PyTuple_SetItem(grads, i, (&_Py_NoneStruct)); | ||||
1351 | } else { | ||||
1352 | PyTuple_SET_ITEM(grads, i,PyTuple_SetItem(grads, i, reinterpret_cast<PyObject*>(output_gradients [i])) | ||||
1353 | reinterpret_cast<PyObject*>(output_gradients[i]))PyTuple_SetItem(grads, i, reinterpret_cast<PyObject*>(output_gradients [i])); | ||||
1354 | } | ||||
1355 | } | ||||
1356 | PyObject* py_result = (*backward_function)(grads, unneeded_gradients); | ||||
1357 | Py_DECREF(grads)_Py_DECREF(((PyObject*)(grads))); | ||||
1358 | if (py_result == nullptr) { | ||||
1359 | return tensorflow::errors::Internal("gradient function threw exceptions"); | ||||
1360 | } | ||||
1361 | PyObject* seq = | ||||
1362 | PySequence_Fast(py_result, "expected a sequence of gradients"); | ||||
1363 | if (seq == nullptr) { | ||||
1364 | return tensorflow::errors::InvalidArgument( | ||||
1365 | "gradient function did not return a list"); | ||||
1366 | } | ||||
1367 | int len = PySequence_Fast_GET_SIZE(seq)(((((((PyObject*)(seq))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((static_cast<void> (0)), (((PyVarObject *)(seq))->ob_size)) : (((PyVarObject*)(((static_cast<void > (0)), (PyTupleObject *)(seq))))->ob_size)); | ||||
1368 | if (len != result.size()) { | ||||
1369 | return tensorflow::errors::Internal( | ||||
1370 | "Recorded operation '", op_type, | ||||
1371 | "' returned too few gradients. Expected ", result.size(), | ||||
1372 | " but received ", len); | ||||
1373 | } | ||||
1374 | PyObject** seq_array = PySequence_Fast_ITEMS(seq)(((((((PyObject*)(seq))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((PyListObject *)(seq))->ob_item : ((PyTupleObject *)(seq))->ob_item); | ||||
1375 | VLOG(1)(__builtin_expect(!!(!(([](int level, const char* fname) { static const bool vmodule_activated = ::tensorflow::internal::LogMessage ::VmoduleActivated(fname, level); return vmodule_activated; } )(1, "tensorflow/python/eager/pywrap_tfe_src.cc"))), 1)) ? (void )0 : ::tensorflow::internal::Voidifier() & ::tensorflow:: internal::LogMessage("tensorflow/python/eager/pywrap_tfe_src.cc" , 1375, tensorflow::INFO) << "Gradient length is " << len; | ||||
1376 | for (int i = 0; i < len; ++i) { | ||||
1377 | PyObject* item = seq_array[i]; | ||||
1378 | if (item == Py_None(&_Py_NoneStruct)) { | ||||
1379 | result[i] = nullptr; | ||||
1380 | } else { | ||||
1381 | Py_INCREF(item)_Py_INCREF(((PyObject*)(item))); | ||||
1382 | result[i] = item; | ||||
1383 | } | ||||
1384 | } | ||||
1385 | Py_DECREF(seq)_Py_DECREF(((PyObject*)(seq))); | ||||
1386 | Py_DECREF(py_result)_Py_DECREF(((PyObject*)(py_result))); | ||||
1387 | return tensorflow::Status::OK(); | ||||
1388 | } | ||||
1389 | |||||
1390 | void DeleteGradient(PyObject* tensor) const final { Py_XDECREF(tensor)_Py_XDECREF(((PyObject*)(tensor))); } | ||||
1391 | |||||
1392 | PyTapeTensor TapeTensorFromGradient(PyObject* tensor) const final { | ||||
1393 | return TapeTensorFromTensor(tensor); | ||||
1394 | } | ||||
1395 | |||||
1396 | private: | ||||
1397 | PyObject* py_vspace_; | ||||
1398 | |||||
1399 | PyObject* num_elements_; | ||||
1400 | PyObject* aggregate_fn_; | ||||
1401 | PyObject* zeros_fn_; | ||||
1402 | PyObject* zeros_like_fn_; | ||||
1403 | PyObject* ones_fn_; | ||||
1404 | PyObject* ones_like_fn_; | ||||
1405 | PyObject* graph_shape_fn_; | ||||
1406 | }; | ||||
1407 | PyVSpace* py_vspace = nullptr; | ||||
1408 | |||||
1409 | bool HasAccumulator(); | ||||
1410 | |||||
1411 | PyObject* TFE_Py_RegisterVSpace(PyObject* e) { | ||||
1412 | if (py_vspace != nullptr) { | ||||
1413 | if (HasAccumulator()) { | ||||
1414 | // Accumulators reference py_vspace, so we can't swap it out while one is | ||||
1415 | // active. This is unlikely to ever happen. | ||||
1416 | MaybeRaiseExceptionFromStatus( | ||||
1417 | tensorflow::errors::Internal( | ||||
1418 | "Can't change the vspace implementation while a " | ||||
1419 | "forward accumulator is active."), | ||||
1420 | nullptr); | ||||
1421 | } | ||||
1422 | delete py_vspace; | ||||
1423 | } | ||||
1424 | |||||
1425 | py_vspace = new PyVSpace(e); | ||||
1426 | auto status = py_vspace->Initialize(); | ||||
1427 | if (MaybeRaiseExceptionFromStatus(status, nullptr)) { | ||||
1428 | delete py_vspace; | ||||
1429 | return nullptr; | ||||
1430 | } | ||||
1431 | |||||
1432 | Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (& _Py_NoneStruct); | ||||
1433 | } | ||||
1434 | |||||
1435 | PyObject* PyTapeTensor::GetShape() const { | ||||
1436 | if (shape_.index() == 0) { | ||||
1437 | auto& shape = absl::get<0>(shape_); | ||||
1438 | PyObject* py_shape = PyTuple_New(shape.dims()); | ||||
1439 | for (int i = 0; i < shape.dims(); ++i) { | ||||
1440 | PyTuple_SET_ITEM(py_shape, i, PyLong_FromLong(shape.dim_size(i)))PyTuple_SetItem(py_shape, i, PyLong_FromLong(shape.dim_size(i ))); | ||||
1441 | } | ||||
1442 | |||||
1443 | return py_shape; | ||||
1444 | } | ||||
1445 | |||||
1446 | return py_vspace->GraphShape(absl::get<1>(shape_)); | ||||
1447 | } | ||||
1448 | |||||
1449 | PyObject* PyTapeTensor::OnesLike() const { | ||||
1450 | if (shape_.index() == 1) { | ||||
1451 | PyObject* tensor = absl::get<1>(shape_); | ||||
1452 | return py_vspace->OnesLike(tensor); | ||||
1453 | } | ||||
1454 | PyObject* py_shape = GetShape(); | ||||
1455 | PyObject* dtype_field = GetPyDType(); | ||||
1456 | PyObject* result = py_vspace->Ones(py_shape, dtype_field); | ||||
1457 | Py_DECREF(dtype_field)_Py_DECREF(((PyObject*)(dtype_field))); | ||||
1458 | Py_DECREF(py_shape)_Py_DECREF(((PyObject*)(py_shape))); | ||||
1459 | return result; | ||||
1460 | } | ||||
1461 | |||||
1462 | PyObject* PyTapeTensor::ZerosLike() const { | ||||
1463 | if (GetDType() == tensorflow::DT_RESOURCE) { | ||||
1464 | // Gradient functions for ops which return resource tensors accept | ||||
1465 | // None. This is the behavior of py_vspace->Zeros, but checking here avoids | ||||
1466 | // issues with ZerosLike. | ||||
1467 | Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (& _Py_NoneStruct); | ||||
1468 | } | ||||
1469 | if (shape_.index() == 1) { | ||||
1470 | PyObject* tensor = absl::get<1>(shape_); | ||||
1471 | return py_vspace->ZerosLike(tensor); | ||||
1472 | } | ||||
1473 | PyObject* py_shape = GetShape(); | ||||
1474 | PyObject* dtype_field = GetPyDType(); | ||||
1475 | PyObject* result = py_vspace->Zeros(py_shape, dtype_field); | ||||
1476 | Py_DECREF(dtype_field)_Py_DECREF(((PyObject*)(dtype_field))); | ||||
1477 | Py_DECREF(py_shape)_Py_DECREF(((PyObject*)(py_shape))); | ||||
1478 | return result; | ||||
1479 | } | ||||
1480 | |||||
1481 | // Keeps track of all variables that have been accessed during execution. | ||||
1482 | class VariableWatcher { | ||||
1483 | public: | ||||
1484 | VariableWatcher() {} | ||||
1485 | |||||
1486 | ~VariableWatcher() { | ||||
1487 | for (const IdAndVariable& v : watched_variables_) { | ||||
1488 | Py_DECREF(v.variable)_Py_DECREF(((PyObject*)(v.variable))); | ||||
1489 | } | ||||
1490 | } | ||||
1491 | |||||
1492 | int64_t WatchVariable(PyObject* v) { | ||||
1493 | tensorflow::Safe_PyObjectPtr handle(PyObject_GetAttrString(v, "handle")); | ||||
1494 | if (handle == nullptr) { | ||||
1495 | return -1; | ||||
1496 | } | ||||
1497 | int64_t id = FastTensorId(handle.get()); | ||||
1498 | |||||
1499 | tensorflow::mutex_lock l(watched_variables_mu_); | ||||
1500 | auto insert_result = watched_variables_.emplace(id, v); | ||||
1501 | |||||
1502 | if (insert_result.second) { | ||||
1503 | // Only increment the reference count if we aren't already watching this | ||||
1504 | // variable. | ||||
1505 | Py_INCREF(v)_Py_INCREF(((PyObject*)(v))); | ||||
1506 | } | ||||
1507 | |||||
1508 | return id; | ||||
1509 | } | ||||
1510 | |||||
1511 | PyObject* GetVariablesAsPyTuple() { | ||||
1512 | tensorflow::mutex_lock l(watched_variables_mu_); | ||||
1513 | PyObject* result = PyTuple_New(watched_variables_.size()); | ||||
1514 | Py_ssize_t pos = 0; | ||||
1515 | for (const IdAndVariable& id_and_variable : watched_variables_) { | ||||
1516 | PyTuple_SET_ITEM(result, pos++, id_and_variable.variable)PyTuple_SetItem(result, pos++, id_and_variable.variable); | ||||
1517 | Py_INCREF(id_and_variable.variable)_Py_INCREF(((PyObject*)(id_and_variable.variable))); | ||||
1518 | } | ||||
1519 | return result; | ||||
1520 | } | ||||
1521 | |||||
1522 | private: | ||||
1523 | // We store an IdAndVariable in the map since the map needs to be locked | ||||
1524 | // during insert, but should not call back into python during insert to avoid | ||||
1525 | // deadlocking with the GIL. | ||||
1526 | struct IdAndVariable { | ||||
1527 | int64_t id; | ||||
1528 | PyObject* variable; | ||||
1529 | |||||
1530 | IdAndVariable(int64_t id, PyObject* variable) | ||||
1531 | : id(id), variable(variable) {} | ||||
1532 | }; | ||||
1533 | struct CompareById { | ||||
1534 | bool operator()(const IdAndVariable& lhs, const IdAndVariable& rhs) const { | ||||
1535 | return lhs.id < rhs.id; | ||||
1536 | } | ||||
1537 | }; | ||||
1538 | |||||
1539 | tensorflow::mutex watched_variables_mu_; | ||||
1540 | std::set<IdAndVariable, CompareById> watched_variables_ | ||||
1541 | TF_GUARDED_BY(watched_variables_mu_)__attribute__((guarded_by(watched_variables_mu_))); | ||||
1542 | }; | ||||
1543 | |||||
1544 | class GradientTape | ||||
1545 | : public tensorflow::eager::GradientTape<PyObject, PyBackwardFunction, | ||||
1546 | PyTapeTensor> { | ||||
1547 | public: | ||||
1548 | explicit GradientTape(bool persistent, bool watch_accessed_variables) | ||||
1549 | : tensorflow::eager::GradientTape<PyObject, PyBackwardFunction, | ||||
1550 | PyTapeTensor>(persistent), | ||||
1551 | watch_accessed_variables_(watch_accessed_variables) {} | ||||
1552 | |||||
1553 | virtual ~GradientTape() {} | ||||
1554 | |||||
1555 | void VariableAccessed(PyObject* v) { | ||||
1556 | if (watch_accessed_variables_) { | ||||
1557 | WatchVariable(v); | ||||
1558 | } | ||||
1559 | } | ||||
1560 | |||||
1561 | void WatchVariable(PyObject* v) { | ||||
1562 | int64_t id = variable_watcher_.WatchVariable(v); | ||||
1563 | |||||
1564 | if (!PyErr_Occurred()) { | ||||
1565 | this->Watch(id); | ||||
1566 | } | ||||
1567 | } | ||||
1568 | |||||
1569 | PyObject* GetVariablesAsPyTuple() { | ||||
1570 | return variable_watcher_.GetVariablesAsPyTuple(); | ||||
1571 | } | ||||
1572 | |||||
1573 | private: | ||||
1574 | bool watch_accessed_variables_; | ||||
1575 | VariableWatcher variable_watcher_; | ||||
1576 | }; | ||||
1577 | |||||
1578 | typedef tensorflow::eager::ForwardAccumulator<PyObject, PyBackwardFunction, | ||||
1579 | PyTapeTensor> | ||||
1580 | ForwardAccumulator; | ||||
1581 | |||||
1582 | // Incremented when a GradientTape or accumulator is newly added to a set, and | ||||
1583 | // used to enforce an ordering between them. | ||||
1584 | std::atomic_uint_fast64_t tape_nesting_id_counter(0); | ||||
1585 | |||||
1586 | typedef struct { | ||||
1587 | PyObject_HEADPyObject ob_base; | ||||
1588 | /* Type-specific fields go here. */ | ||||
1589 | GradientTape* tape; | ||||
1590 | // A nesting order between GradientTapes and ForwardAccumulators, used to | ||||
1591 | // ensure that GradientTapes do not watch the products of outer | ||||
1592 | // ForwardAccumulators. | ||||
1593 | int64_t nesting_id; | ||||
1594 | } TFE_Py_Tape; | ||||
1595 | |||||
1596 | static void TFE_Py_Tape_Delete(PyObject* tape) { | ||||
1597 | delete reinterpret_cast<TFE_Py_Tape*>(tape)->tape; | ||||
1598 | Py_TYPE(tape)(((PyObject*)(tape))->ob_type)->tp_free(tape); | ||||
1599 | } | ||||
1600 | |||||
1601 | static PyTypeObject TFE_Py_Tape_Type = { | ||||
1602 | PyVarObject_HEAD_INIT(nullptr, 0){ { 1, nullptr }, 0 }, "tfe.Tape", /* tp_name */ | ||||
1603 | sizeof(TFE_Py_Tape), /* tp_basicsize */ | ||||
1604 | 0, /* tp_itemsize */ | ||||
1605 | &TFE_Py_Tape_Delete, /* tp_dealloc */ | ||||
1606 | #if PY_VERSION_HEX((3 << 24) | (8 << 16) | (5 << 8) | (0xF << 4) | (0 << 0)) < 0x03080000 | ||||
1607 | nullptr, /* tp_print */ | ||||
1608 | #else | ||||
1609 | 0, /* tp_vectorcall_offset */ | ||||
1610 | #endif | ||||
1611 | nullptr, /* tp_getattr */ | ||||
1612 | nullptr, /* tp_setattr */ | ||||
1613 | nullptr, /* tp_reserved */ | ||||
1614 | nullptr, /* tp_repr */ | ||||
1615 | nullptr, /* tp_as_number */ | ||||
1616 | nullptr, /* tp_as_sequence */ | ||||
1617 | nullptr, /* tp_as_mapping */ | ||||
1618 | nullptr, /* tp_hash */ | ||||
1619 | nullptr, /* tp_call */ | ||||
1620 | nullptr, /* tp_str */ | ||||
1621 | nullptr, /* tp_getattro */ | ||||
1622 | nullptr, /* tp_setattro */ | ||||
1623 | nullptr, /* tp_as_buffer */ | ||||
1624 | Py_TPFLAGS_DEFAULT( 0 | (1UL << 18) | 0), /* tp_flags */ | ||||
1625 | "TFE_Py_Tape objects", /* tp_doc */ | ||||
1626 | }; | ||||
1627 | |||||
1628 | typedef struct { | ||||
1629 | PyObject_HEADPyObject ob_base; | ||||
1630 | /* Type-specific fields go here. */ | ||||
1631 | ForwardAccumulator* accumulator; | ||||
1632 | // A nesting order between GradientTapes and ForwardAccumulators, used to | ||||
1633 | // ensure that GradientTapes do not watch the products of outer | ||||
1634 | // ForwardAccumulators. | ||||
1635 | int64_t nesting_id; | ||||
1636 | } TFE_Py_ForwardAccumulator; | ||||
1637 | |||||
1638 | static void TFE_Py_ForwardAccumulatorDelete(PyObject* accumulator) { | ||||
1639 | delete reinterpret_cast<TFE_Py_ForwardAccumulator*>(accumulator)->accumulator; | ||||
1640 | Py_TYPE(accumulator)(((PyObject*)(accumulator))->ob_type)->tp_free(accumulator); | ||||
1641 | } | ||||
1642 | |||||
1643 | static PyTypeObject TFE_Py_ForwardAccumulator_Type = { | ||||
1644 | PyVarObject_HEAD_INIT(nullptr, 0){ { 1, nullptr }, 0 }, "ForwardAccumulator", /* tp_name */ | ||||
1645 | sizeof(TFE_Py_ForwardAccumulator), /* tp_basicsize */ | ||||
1646 | 0, /* tp_itemsize */ | ||||
1647 | &TFE_Py_ForwardAccumulatorDelete, /* tp_dealloc */ | ||||
1648 | #if PY_VERSION_HEX((3 << 24) | (8 << 16) | (5 << 8) | (0xF << 4) | (0 << 0)) < 0x03080000 | ||||
1649 | nullptr, /* tp_print */ | ||||
1650 | #else | ||||
1651 | 0, /* tp_vectorcall_offset */ | ||||
1652 | #endif | ||||
1653 | nullptr, /* tp_getattr */ | ||||
1654 | nullptr, /* tp_setattr */ | ||||
1655 | nullptr, /* tp_reserved */ | ||||
1656 | nullptr, /* tp_repr */ | ||||
1657 | nullptr, /* tp_as_number */ | ||||
1658 | nullptr, /* tp_as_sequence */ | ||||
1659 | nullptr, /* tp_as_mapping */ | ||||
1660 | nullptr, /* tp_hash */ | ||||
1661 | nullptr, /* tp_call */ | ||||
1662 | nullptr, /* tp_str */ | ||||
1663 | nullptr, /* tp_getattro */ | ||||
1664 | nullptr, /* tp_setattro */ | ||||
1665 | nullptr, /* tp_as_buffer */ | ||||
1666 | Py_TPFLAGS_DEFAULT( 0 | (1UL << 18) | 0), /* tp_flags */ | ||||
1667 | "TFE_Py_ForwardAccumulator objects", /* tp_doc */ | ||||
1668 | }; | ||||
1669 | |||||
1670 | typedef struct { | ||||
1671 | PyObject_HEADPyObject ob_base; | ||||
1672 | /* Type-specific fields go here. */ | ||||
1673 | VariableWatcher* variable_watcher; | ||||
1674 | } TFE_Py_VariableWatcher; | ||||
1675 | |||||
1676 | static void TFE_Py_VariableWatcher_Delete(PyObject* variable_watcher) { | ||||
1677 | delete reinterpret_cast<TFE_Py_VariableWatcher*>(variable_watcher) | ||||
1678 | ->variable_watcher; | ||||
1679 | Py_TYPE(variable_watcher)(((PyObject*)(variable_watcher))->ob_type)->tp_free(variable_watcher); | ||||
1680 | } | ||||
1681 | |||||
1682 | static PyTypeObject TFE_Py_VariableWatcher_Type = { | ||||
1683 | PyVarObject_HEAD_INIT(nullptr, 0){ { 1, nullptr }, 0 }, "tfe.VariableWatcher", /* tp_name */ | ||||
1684 | sizeof(TFE_Py_VariableWatcher), /* tp_basicsize */ | ||||
1685 | 0, /* tp_itemsize */ | ||||
1686 | &TFE_Py_VariableWatcher_Delete, /* tp_dealloc */ | ||||
1687 | #if PY_VERSION_HEX((3 << 24) | (8 << 16) | (5 << 8) | (0xF << 4) | (0 << 0)) < 0x03080000 | ||||
1688 | nullptr, /* tp_print */ | ||||
1689 | #else | ||||
1690 | 0, /* tp_vectorcall_offset */ | ||||
1691 | #endif | ||||
1692 | nullptr, /* tp_getattr */ | ||||
1693 | nullptr, /* tp_setattr */ | ||||
1694 | nullptr, /* tp_reserved */ | ||||
1695 | nullptr, /* tp_repr */ | ||||
1696 | nullptr, /* tp_as_number */ | ||||
1697 | nullptr, /* tp_as_sequence */ | ||||
1698 | nullptr, /* tp_as_mapping */ | ||||
1699 | nullptr, /* tp_hash */ | ||||
1700 | nullptr, /* tp_call */ | ||||
1701 | nullptr, /* tp_str */ | ||||
1702 | nullptr, /* tp_getattro */ | ||||
1703 | nullptr, /* tp_setattro */ | ||||
1704 | nullptr, /* tp_as_buffer */ | ||||
1705 | Py_TPFLAGS_DEFAULT( 0 | (1UL << 18) | 0), /* tp_flags */ | ||||
1706 | "TFE_Py_VariableWatcher objects", /* tp_doc */ | ||||
1707 | }; | ||||
1708 | |||||
1709 | // Note: in the current design no mutex is needed here because of the python | ||||
1710 | // GIL, which is always held when any TFE_Py_* methods are called. We should | ||||
1711 | // revisit this if/when decide to not hold the GIL while manipulating the tape | ||||
1712 | // stack. | ||||
1713 | tensorflow::gtl::CompactPointerSet<TFE_Py_Tape*>* GetTapeSet() { | ||||
1714 | thread_local std::unique_ptr<tensorflow::gtl::CompactPointerSet<TFE_Py_Tape*>> | ||||
1715 | tape_set = nullptr; | ||||
1716 | if (tape_set == nullptr) { | ||||
1717 | tape_set.reset(new tensorflow::gtl::CompactPointerSet<TFE_Py_Tape*>); | ||||
1718 | } | ||||
1719 | return tape_set.get(); | ||||
1720 | } | ||||
1721 | |||||
1722 | tensorflow::gtl::CompactPointerSet<TFE_Py_VariableWatcher*>* | ||||
1723 | GetVariableWatcherSet() { | ||||
1724 | thread_local std::unique_ptr< | ||||
1725 | tensorflow::gtl::CompactPointerSet<TFE_Py_VariableWatcher*>> | ||||
1726 | variable_watcher_set = nullptr; | ||||
1727 | if (variable_watcher_set == nullptr) { | ||||
1728 | variable_watcher_set.reset( | ||||
1729 | new tensorflow::gtl::CompactPointerSet<TFE_Py_VariableWatcher*>); | ||||
1730 | } | ||||
1731 | return variable_watcher_set.get(); | ||||
1732 | } | ||||
1733 | |||||
1734 | // A linked hash set, where iteration is in insertion order. | ||||
1735 | // | ||||
1736 | // Nested accumulators rely on op recording happening in insertion order, so an | ||||
1737 | // unordered data structure like CompactPointerSet is not suitable. Outer | ||||
1738 | // accumulators need to observe operations first so they know to watch the inner | ||||
1739 | // accumulator's jvp computation. | ||||
1740 | // | ||||
1741 | // Not thread safe. | ||||
1742 | class AccumulatorSet { | ||||
1743 | public: | ||||
1744 | // Returns true if `element` was newly inserted, false if it already exists. | ||||
1745 | bool insert(TFE_Py_ForwardAccumulator* element) { | ||||
1746 | if (map_.find(element) != map_.end()) { | ||||
1747 | return false; | ||||
1748 | } | ||||
1749 | ListType::iterator it = ordered_.insert(ordered_.end(), element); | ||||
1750 | map_.insert(std::make_pair(element, it)); | ||||
1751 | return true; | ||||
1752 | } | ||||
1753 | |||||
1754 | void erase(TFE_Py_ForwardAccumulator* element) { | ||||
1755 | MapType::iterator existing = map_.find(element); | ||||
1756 | if (existing == map_.end()) { | ||||
1757 | return; | ||||
1758 | } | ||||
1759 | ListType::iterator list_position = existing->second; | ||||
1760 | map_.erase(existing); | ||||
1761 | ordered_.erase(list_position); | ||||
1762 | } | ||||
1763 | |||||
1764 | bool empty() const { return ordered_.empty(); } | ||||
1765 | |||||
1766 | size_t size() const { return ordered_.size(); } | ||||
1767 | |||||
1768 | private: | ||||
1769 | typedef std::list<TFE_Py_ForwardAccumulator*> ListType; | ||||
1770 | typedef tensorflow::gtl::FlatMap<TFE_Py_ForwardAccumulator*, | ||||
1771 | ListType::iterator> | ||||
1772 | MapType; | ||||
1773 | |||||
1774 | public: | ||||
1775 | typedef ListType::const_iterator const_iterator; | ||||
1776 | typedef ListType::const_reverse_iterator const_reverse_iterator; | ||||
1777 | |||||
1778 | const_iterator begin() const { return ordered_.begin(); } | ||||
1779 | const_iterator end() const { return ordered_.end(); } | ||||
1780 | |||||
1781 | const_reverse_iterator rbegin() const { return ordered_.rbegin(); } | ||||
1782 | const_reverse_iterator rend() const { return ordered_.rend(); } | ||||
1783 | |||||
1784 | private: | ||||
1785 | MapType map_; | ||||
1786 | ListType ordered_; | ||||
1787 | }; | ||||
1788 | |||||
1789 | AccumulatorSet* GetAccumulatorSet() { | ||||
1790 | thread_local std::unique_ptr<AccumulatorSet> accumulator_set{nullptr}; | ||||
1791 | if (accumulator_set == nullptr) { | ||||
1792 | accumulator_set.reset(new AccumulatorSet); | ||||
1793 | } | ||||
1794 | return accumulator_set.get(); | ||||
1795 | } | ||||
1796 | |||||
1797 | inline bool HasAccumulator() { return !GetAccumulatorSet()->empty(); } | ||||
1798 | |||||
1799 | inline bool HasGradientTape() { return !GetTapeSet()->empty(); } | ||||
1800 | |||||
1801 | inline bool HasAccumulatorOrTape() { | ||||
1802 | return HasGradientTape() || HasAccumulator(); | ||||
1803 | } | ||||
1804 | |||||
1805 | // A safe copy of a set, used for tapes and accumulators. The copy is not | ||||
1806 | // affected by other python threads changing the set of active tapes. | ||||
1807 | template <typename ContainerType> | ||||
1808 | class SafeSetCopy { | ||||
1809 | public: | ||||
1810 | explicit SafeSetCopy(const ContainerType& to_copy) : set_copy_(to_copy) { | ||||
1811 | for (auto* member : set_copy_) { | ||||
1812 | Py_INCREF(member)_Py_INCREF(((PyObject*)(member))); | ||||
1813 | } | ||||
1814 | } | ||||
1815 | |||||
1816 | ~SafeSetCopy() { | ||||
1817 | for (auto* member : set_copy_) { | ||||
1818 | Py_DECREF(member)_Py_DECREF(((PyObject*)(member))); | ||||
1819 | } | ||||
1820 | } | ||||
1821 | |||||
1822 | typename ContainerType::const_iterator begin() const { | ||||
1823 | return set_copy_.begin(); | ||||
1824 | } | ||||
1825 | |||||
1826 | typename ContainerType::const_iterator end() const { return set_copy_.end(); } | ||||
1827 | |||||
1828 | bool empty() const { return set_copy_.empty(); } | ||||
1829 | size_t size() const { return set_copy_.size(); } | ||||
1830 | |||||
1831 | protected: | ||||
1832 | ContainerType set_copy_; | ||||
1833 | }; | ||||
1834 | |||||
1835 | class SafeTapeSet | ||||
1836 | : public SafeSetCopy<tensorflow::gtl::CompactPointerSet<TFE_Py_Tape*>> { | ||||
1837 | public: | ||||
1838 | SafeTapeSet() | ||||
1839 | : SafeSetCopy<tensorflow::gtl::CompactPointerSet<TFE_Py_Tape*>>( | ||||
1840 | *GetTapeSet()) {} | ||||
1841 | }; | ||||
1842 | |||||
1843 | class SafeAccumulatorSet : public SafeSetCopy<AccumulatorSet> { | ||||
1844 | public: | ||||
1845 | SafeAccumulatorSet() : SafeSetCopy<AccumulatorSet>(*GetAccumulatorSet()) {} | ||||
1846 | |||||
1847 | typename AccumulatorSet::const_reverse_iterator rbegin() const { | ||||
1848 | return set_copy_.rbegin(); | ||||
1849 | } | ||||
1850 | |||||
1851 | typename AccumulatorSet::const_reverse_iterator rend() const { | ||||
1852 | return set_copy_.rend(); | ||||
1853 | } | ||||
1854 | }; | ||||
1855 | |||||
1856 | class SafeVariableWatcherSet | ||||
1857 | : public SafeSetCopy< | ||||
1858 | tensorflow::gtl::CompactPointerSet<TFE_Py_VariableWatcher*>> { | ||||
1859 | public: | ||||
1860 | SafeVariableWatcherSet() | ||||
1861 | : SafeSetCopy< | ||||
1862 | tensorflow::gtl::CompactPointerSet<TFE_Py_VariableWatcher*>>( | ||||
1863 | *GetVariableWatcherSet()) {} | ||||
1864 | }; | ||||
1865 | |||||
1866 | bool* ThreadTapeIsStopped() { | ||||
1867 | thread_local bool thread_tape_is_stopped{false}; | ||||
1868 | return &thread_tape_is_stopped; | ||||
1869 | } | ||||
1870 | |||||
1871 | void TFE_Py_TapeSetStopOnThread() { *ThreadTapeIsStopped() = true; } | ||||
1872 | |||||
1873 | void TFE_Py_TapeSetRestartOnThread() { *ThreadTapeIsStopped() = false; } | ||||
1874 | |||||
1875 | PyObject* TFE_Py_TapeSetIsStopped() { | ||||
1876 | if (*ThreadTapeIsStopped()) { | ||||
1877 | Py_RETURN_TRUEreturn _Py_INCREF(((PyObject*)(((PyObject *) &_Py_TrueStruct )))), ((PyObject *) &_Py_TrueStruct); | ||||
1878 | } | ||||
1879 | Py_RETURN_FALSEreturn _Py_INCREF(((PyObject*)(((PyObject *) &_Py_FalseStruct )))), ((PyObject *) &_Py_FalseStruct); | ||||
1880 | } | ||||
1881 | |||||
1882 | PyObject* TFE_Py_TapeSetNew(PyObject* persistent, | ||||
1883 | PyObject* watch_accessed_variables) { | ||||
1884 | TFE_Py_Tape_Type.tp_new = PyType_GenericNew; | ||||
1885 | if (PyType_Ready(&TFE_Py_Tape_Type) < 0) return nullptr; | ||||
1886 | TFE_Py_Tape* tape = PyObject_NEW(TFE_Py_Tape, &TFE_Py_Tape_Type)( (TFE_Py_Tape *) PyObject_Init( (PyObject *) PyObject_Malloc ( ( (&TFE_Py_Tape_Type)->tp_basicsize ) ), (&TFE_Py_Tape_Type )) ); | ||||
1887 | tape->tape = new GradientTape(persistent == Py_True((PyObject *) &_Py_TrueStruct), | ||||
1888 | watch_accessed_variables == Py_True((PyObject *) &_Py_TrueStruct)); | ||||
1889 | Py_INCREF(tape)_Py_INCREF(((PyObject*)(tape))); | ||||
1890 | tape->nesting_id = tape_nesting_id_counter.fetch_add(1); | ||||
1891 | GetTapeSet()->insert(tape); | ||||
1892 | return reinterpret_cast<PyObject*>(tape); | ||||
1893 | } | ||||
1894 | |||||
1895 | void TFE_Py_TapeSetAdd(PyObject* tape) { | ||||
1896 | Py_INCREF(tape)_Py_INCREF(((PyObject*)(tape))); | ||||
1897 | TFE_Py_Tape* tfe_tape = reinterpret_cast<TFE_Py_Tape*>(tape); | ||||
1898 | if (!GetTapeSet()->insert(tfe_tape).second) { | ||||
1899 | // Already exists in the tape set. | ||||
1900 | Py_DECREF(tape)_Py_DECREF(((PyObject*)(tape))); | ||||
1901 | } else { | ||||
1902 | tfe_tape->nesting_id = tape_nesting_id_counter.fetch_add(1); | ||||
1903 | } | ||||
1904 | } | ||||
1905 | |||||
1906 | PyObject* TFE_Py_TapeSetIsEmpty() { | ||||
1907 | if (*ThreadTapeIsStopped() || !HasAccumulatorOrTape()) { | ||||
1908 | Py_RETURN_TRUEreturn _Py_INCREF(((PyObject*)(((PyObject *) &_Py_TrueStruct )))), ((PyObject *) &_Py_TrueStruct); | ||||
1909 | } | ||||
1910 | Py_RETURN_FALSEreturn _Py_INCREF(((PyObject*)(((PyObject *) &_Py_FalseStruct )))), ((PyObject *) &_Py_FalseStruct); | ||||
1911 | } | ||||
1912 | |||||
1913 | void TFE_Py_TapeSetRemove(PyObject* tape) { | ||||
1914 | auto* stack = GetTapeSet(); | ||||
1915 | stack->erase(reinterpret_cast<TFE_Py_Tape*>(tape)); | ||||
1916 | // We kept a reference to the tape in the set to ensure it wouldn't get | ||||
1917 | // deleted under us; cleaning it up here. | ||||
1918 | Py_DECREF(tape)_Py_DECREF(((PyObject*)(tape))); | ||||
1919 | } | ||||
1920 | |||||
1921 | static std::vector<int64_t> MakeIntList(PyObject* list) { | ||||
1922 | if (list == Py_None(&_Py_NoneStruct)) { | ||||
1923 | return {}; | ||||
1924 | } | ||||
1925 | PyObject* seq = PySequence_Fast(list, "expected a sequence"); | ||||
1926 | if (seq == nullptr) { | ||||
1927 | return {}; | ||||
1928 | } | ||||
1929 | int len = PySequence_Size(list); | ||||
1930 | PyObject** seq_array = PySequence_Fast_ITEMS(seq)(((((((PyObject*)(seq))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((PyListObject *)(seq))->ob_item : ((PyTupleObject *)(seq))->ob_item); | ||||
1931 | std::vector<int64_t> tensor_ids; | ||||
1932 | tensor_ids.reserve(len); | ||||
1933 | for (int i = 0; i < len; ++i) { | ||||
1934 | PyObject* item = seq_array[i]; | ||||
1935 | #if PY_MAJOR_VERSION3 >= 3 | ||||
1936 | if (PyLong_Check(item)((((((PyObject*)(item))->ob_type))->tp_flags & ((1UL << 24))) != 0)) { | ||||
1937 | #else | ||||
1938 | if (PyLong_Check(item)((((((PyObject*)(item))->ob_type))->tp_flags & ((1UL << 24))) != 0) || PyInt_Check(item)) { | ||||
1939 | #endif | ||||
1940 | int64_t id = MakeInt(item); | ||||
1941 | tensor_ids.push_back(id); | ||||
1942 | } else { | ||||
1943 | tensor_ids.push_back(-1); | ||||
1944 | } | ||||
1945 | } | ||||
1946 | Py_DECREF(seq)_Py_DECREF(((PyObject*)(seq))); | ||||
1947 | return tensor_ids; | ||||
1948 | } | ||||
1949 | |||||
1950 | // Fill `tensor_ids` and `dtypes` from `tensors`, none of which may be | ||||
1951 | // null. Returns true on success and false on a Python exception. | ||||
1952 | bool TensorShapesAndDtypes(PyObject* tensors, std::vector<int64_t>* tensor_ids, | ||||
1953 | std::vector<tensorflow::DataType>* dtypes) { | ||||
1954 | tensorflow::Safe_PyObjectPtr seq( | ||||
1955 | PySequence_Fast(tensors, "expected a sequence")); | ||||
1956 | if (seq == nullptr) { | ||||
1957 | return false; | ||||
1958 | } | ||||
1959 | int len = PySequence_Fast_GET_SIZE(seq.get())(((((((PyObject*)(seq.get()))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((static_cast<void> (0)), (((PyVarObject*)(seq.get()))->ob_size)) : (((PyVarObject* )(((static_cast<void> (0)), (PyTupleObject *)(seq.get() ))))->ob_size)); | ||||
1960 | PyObject** seq_array = PySequence_Fast_ITEMS(seq.get())(((((((PyObject*)(seq.get()))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((PyListObject *)(seq.get()))-> ob_item : ((PyTupleObject *)(seq.get()))->ob_item); | ||||
1961 | tensor_ids->reserve(len); | ||||
1962 | dtypes->reserve(len); | ||||
1963 | for (int i = 0; i < len; ++i) { | ||||
1964 | PyObject* item = seq_array[i]; | ||||
1965 | tensor_ids->push_back(FastTensorId(item)); | ||||
1966 | dtypes->push_back(tensorflow::PyTensor_DataType(item)); | ||||
1967 | } | ||||
1968 | return true; | ||||
1969 | } | ||||
1970 | |||||
1971 | bool TapeCouldPossiblyRecord(PyObject* tensors) { | ||||
1972 | if (tensors == Py_None(&_Py_NoneStruct)) { | ||||
1973 | return false; | ||||
1974 | } | ||||
1975 | if (*ThreadTapeIsStopped()) { | ||||
1976 | return false; | ||||
1977 | } | ||||
1978 | if (!HasAccumulatorOrTape()) { | ||||
1979 | return false; | ||||
1980 | } | ||||
1981 | return true; | ||||
1982 | } | ||||
1983 | |||||
1984 | bool CouldBackprop() { return !*ThreadTapeIsStopped() && HasGradientTape(); } | ||||
1985 | |||||
1986 | bool CouldForwardprop() { return !*ThreadTapeIsStopped() && HasAccumulator(); } | ||||
1987 | |||||
1988 | PyObject* TFE_Py_TapeSetShouldRecordBackprop(PyObject* tensors) { | ||||
1989 | if (!TapeCouldPossiblyRecord(tensors) || !CouldBackprop()) { | ||||
1990 | Py_RETURN_FALSEreturn _Py_INCREF(((PyObject*)(((PyObject *) &_Py_FalseStruct )))), ((PyObject *) &_Py_FalseStruct); | ||||
1991 | } | ||||
1992 | // TODO(apassos) consider not building a list and changing the API to check | ||||
1993 | // each tensor individually. | ||||
1994 | std::vector<int64_t> tensor_ids; | ||||
1995 | std::vector<tensorflow::DataType> dtypes; | ||||
1996 | if (!TensorShapesAndDtypes(tensors, &tensor_ids, &dtypes)) { | ||||
1997 | return nullptr; | ||||
1998 | } | ||||
1999 | auto tape_set = *GetTapeSet(); | ||||
2000 | for (TFE_Py_Tape* tape : tape_set) { | ||||
2001 | if (tape->tape->ShouldRecord(tensor_ids, dtypes)) { | ||||
2002 | Py_RETURN_TRUEreturn _Py_INCREF(((PyObject*)(((PyObject *) &_Py_TrueStruct )))), ((PyObject *) &_Py_TrueStruct); | ||||
2003 | } | ||||
2004 | } | ||||
2005 | |||||
2006 | Py_RETURN_FALSEreturn _Py_INCREF(((PyObject*)(((PyObject *) &_Py_FalseStruct )))), ((PyObject *) &_Py_FalseStruct); | ||||
2007 | } | ||||
2008 | |||||
2009 | PyObject* TFE_Py_ForwardAccumulatorPushState() { | ||||
2010 | auto forward_accumulators = *GetAccumulatorSet(); | ||||
2011 | for (TFE_Py_ForwardAccumulator* accumulator : forward_accumulators) { | ||||
2012 | accumulator->accumulator->PushState(); | ||||
2013 | } | ||||
2014 | Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (& _Py_NoneStruct); | ||||
2015 | } | ||||
2016 | |||||
2017 | PyObject* TFE_Py_ForwardAccumulatorPopState() { | ||||
2018 | auto forward_accumulators = *GetAccumulatorSet(); | ||||
2019 | for (TFE_Py_ForwardAccumulator* accumulator : forward_accumulators) { | ||||
2020 | accumulator->accumulator->PopState(); | ||||
2021 | } | ||||
2022 | Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (& _Py_NoneStruct); | ||||
2023 | } | ||||
2024 | |||||
2025 | PyObject* TFE_Py_TapeSetPossibleGradientTypes(PyObject* tensors) { | ||||
2026 | if (!TapeCouldPossiblyRecord(tensors)) { | ||||
2027 | return GetPythonObjectFromInt(0); | ||||
2028 | } | ||||
2029 | std::vector<int64_t> tensor_ids; | ||||
2030 | std::vector<tensorflow::DataType> dtypes; | ||||
2031 | if (!TensorShapesAndDtypes(tensors, &tensor_ids, &dtypes)) { | ||||
2032 | return nullptr; | ||||
2033 | } | ||||
2034 | |||||
2035 | // If there is a persistent tape watching, or if there are multiple tapes | ||||
2036 | // watching, we'll return immediately indicating that higher-order tape | ||||
2037 | // gradients are possible. | ||||
2038 | bool some_tape_watching = false; | ||||
2039 | if (CouldBackprop()) { | ||||
2040 | auto tape_set = *GetTapeSet(); | ||||
2041 | for (TFE_Py_Tape* tape : tape_set) { | ||||
2042 | if (tape->tape->ShouldRecord(tensor_ids, dtypes)) { | ||||
2043 | if (tape->tape->IsPersistent() || some_tape_watching) { | ||||
2044 | // Either this is the second tape watching, or this tape is | ||||
2045 | // persistent: higher-order gradients are possible. | ||||
2046 | return GetPythonObjectFromInt(2); | ||||
2047 | } | ||||
2048 | some_tape_watching = true; | ||||
2049 | } | ||||
2050 | } | ||||
2051 | } | ||||
2052 | if (CouldForwardprop()) { | ||||
2053 | auto forward_accumulators = *GetAccumulatorSet(); | ||||
2054 | for (TFE_Py_ForwardAccumulator* accumulator : forward_accumulators) { | ||||
2055 | if (accumulator->accumulator->ShouldRecord(tensor_ids, dtypes)) { | ||||
2056 | if (some_tape_watching) { | ||||
2057 | // This is the second tape watching: higher-order gradients are | ||||
2058 | // possible. Note that there's no equivalent of persistence for | ||||
2059 | // forward-mode. | ||||
2060 | return GetPythonObjectFromInt(2); | ||||
2061 | } | ||||
2062 | some_tape_watching = true; | ||||
2063 | } | ||||
2064 | } | ||||
2065 | } | ||||
2066 | if (some_tape_watching) { | ||||
2067 | // There's exactly one non-persistent tape. The user can request first-order | ||||
2068 | // gradients but won't be able to get higher-order tape gradients. | ||||
2069 | return GetPythonObjectFromInt(1); | ||||
2070 | } else { | ||||
2071 | // There are no tapes. The user can't request tape gradients. | ||||
2072 | return GetPythonObjectFromInt(0); | ||||
2073 | } | ||||
2074 | } | ||||
2075 | |||||
2076 | void TFE_Py_TapeWatch(PyObject* tape, PyObject* tensor) { | ||||
2077 | if (!CouldBackprop()) { | ||||
2078 | return; | ||||
2079 | } | ||||
2080 | int64_t tensor_id = FastTensorId(tensor); | ||||
2081 | if (PyErr_Occurred()) { | ||||
2082 | return; | ||||
2083 | } | ||||
2084 | reinterpret_cast<TFE_Py_Tape*>(tape)->tape->Watch(tensor_id); | ||||
2085 | } | ||||
2086 | |||||
2087 | bool ListContainsNone(PyObject* list) { | ||||
2088 | if (list == Py_None(&_Py_NoneStruct)) return true; | ||||
2089 | tensorflow::Safe_PyObjectPtr seq( | ||||
2090 | PySequence_Fast(list, "expected a sequence")); | ||||
2091 | if (seq == nullptr) { | ||||
2092 | return false; | ||||
2093 | } | ||||
2094 | |||||
2095 | int len = PySequence_Size(list); | ||||
2096 | PyObject** seq_array = PySequence_Fast_ITEMS(seq.get())(((((((PyObject*)(seq.get()))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((PyListObject *)(seq.get()))-> ob_item : ((PyTupleObject *)(seq.get()))->ob_item); | ||||
2097 | for (int i = 0; i < len; ++i) { | ||||
2098 | PyObject* item = seq_array[i]; | ||||
2099 | if (item == Py_None(&_Py_NoneStruct)) return true; | ||||
2100 | } | ||||
2101 | |||||
2102 | return false; | ||||
2103 | } | ||||
2104 | |||||
2105 | // As an optimization, the tape generally keeps only the shape and dtype of | ||||
2106 | // tensors, and uses this information to generate ones/zeros tensors. However, | ||||
2107 | // some tensors require OnesLike/ZerosLike because their gradients do not match | ||||
2108 | // their inference shape/dtype. | ||||
2109 | bool DTypeNeedsHandleData(tensorflow::DataType dtype) { | ||||
2110 | return dtype == tensorflow::DT_VARIANT || dtype == tensorflow::DT_RESOURCE; | ||||
2111 | } | ||||
2112 | |||||
2113 | static PyTapeTensor TapeTensorFromTensor(PyObject* tensor) { | ||||
2114 | if (EagerTensor_CheckExact(tensor)) { | ||||
2115 | tensorflow::ImmediateExecutionTensorHandle* handle = | ||||
2116 | tensorflow::unwrap(EagerTensor_Handle(tensor)); | ||||
2117 | int64_t id = PyEagerTensor_ID(tensor); | ||||
2118 | tensorflow::DataType dtype = | ||||
2119 | static_cast<tensorflow::DataType>(handle->DataType()); | ||||
2120 | if (DTypeNeedsHandleData(dtype)) { | ||||
2121 | return PyTapeTensor(id, dtype, tensor); | ||||
2122 | } | ||||
2123 | |||||
2124 | tensorflow::TensorShape tensor_shape; | ||||
2125 | int num_dims; | ||||
2126 | tensorflow::Status status = handle->NumDims(&num_dims); | ||||
2127 | if (status.ok()) { | ||||
2128 | for (int i = 0; i < num_dims; ++i) { | ||||
2129 | int64_t dim_size; | ||||
2130 | status = handle->Dim(i, &dim_size); | ||||
2131 | if (!status.ok()) break; | ||||
2132 | tensor_shape.AddDim(dim_size); | ||||
2133 | } | ||||
2134 | } | ||||
2135 | |||||
2136 | if (MaybeRaiseExceptionFromStatus(status, nullptr)) { | ||||
2137 | return PyTapeTensor(id, static_cast<tensorflow::DataType>(0), | ||||
2138 | tensorflow::TensorShape({})); | ||||
2139 | } else { | ||||
2140 | return PyTapeTensor(id, dtype, tensor_shape); | ||||
2141 | } | ||||
2142 | } | ||||
2143 | int64_t id = FastTensorId(tensor); | ||||
2144 | if (PyErr_Occurred()) { | ||||
2145 | return PyTapeTensor(id, static_cast<tensorflow::DataType>(0), | ||||
2146 | tensorflow::TensorShape({})); | ||||
2147 | } | ||||
2148 | PyObject* dtype_object = PyObject_GetAttrString(tensor, "dtype"); | ||||
2149 | PyObject* dtype_enum = PyObject_GetAttrString(dtype_object, "_type_enum"); | ||||
2150 | Py_DECREF(dtype_object)_Py_DECREF(((PyObject*)(dtype_object))); | ||||
2151 | tensorflow::DataType dtype = | ||||
2152 | static_cast<tensorflow::DataType>(MakeInt(dtype_enum)); | ||||
2153 | Py_DECREF(dtype_enum)_Py_DECREF(((PyObject*)(dtype_enum))); | ||||
2154 | if (PyErr_Occurred()) { | ||||
2155 | return PyTapeTensor(id, static_cast<tensorflow::DataType>(0), | ||||
2156 | tensorflow::TensorShape({})); | ||||
2157 | } | ||||
2158 | static char _shape_tuple[] = "_shape_tuple"; | ||||
2159 | tensorflow::Safe_PyObjectPtr shape_tuple( | ||||
2160 | PyObject_CallMethod(tensor, _shape_tuple, nullptr)); | ||||
2161 | if (PyErr_Occurred()) { | ||||
2162 | return PyTapeTensor(id, static_cast<tensorflow::DataType>(0), | ||||
2163 | tensorflow::TensorShape({})); | ||||
2164 | } | ||||
2165 | |||||
2166 | if (ListContainsNone(shape_tuple.get()) || DTypeNeedsHandleData(dtype)) { | ||||
2167 | return PyTapeTensor(id, dtype, tensor); | ||||
2168 | } | ||||
2169 | |||||
2170 | auto l = MakeIntList(shape_tuple.get()); | ||||
2171 | // Replace -1, which represents accidental Nones which can occur in graph mode | ||||
2172 | // and can cause errors in shape construction with 0s. | ||||
2173 | for (auto& c : l) { | ||||
2174 | if (c < 0) { | ||||
2175 | c = 0; | ||||
2176 | } | ||||
2177 | } | ||||
2178 | tensorflow::TensorShape shape(l); | ||||
2179 | return PyTapeTensor(id, dtype, shape); | ||||
2180 | } | ||||
2181 | |||||
2182 | // Populates output_info from output_seq, which must come from PySequence_Fast. | ||||
2183 | // | ||||
2184 | // Does not take ownership of output_seq. Returns true on success and false if a | ||||
2185 | // Python exception has been set. | ||||
2186 | bool TapeTensorsFromTensorSequence(PyObject* output_seq, | ||||
2187 | std::vector<PyTapeTensor>* output_info) { | ||||
2188 | Py_ssize_t output_len = PySequence_Fast_GET_SIZE(output_seq)(((((((PyObject*)(output_seq))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((static_cast<void> (0)), (((PyVarObject*)(output_seq))->ob_size)) : (((PyVarObject *)(((static_cast<void> (0)), (PyTupleObject *)(output_seq ))))->ob_size)); | ||||
2189 | PyObject** output_seq_array = PySequence_Fast_ITEMS(output_seq)(((((((PyObject*)(output_seq))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((PyListObject *)(output_seq))-> ob_item : ((PyTupleObject *)(output_seq))->ob_item); | ||||
2190 | output_info->reserve(output_len); | ||||
2191 | for (Py_ssize_t i = 0; i < output_len; ++i) { | ||||
2192 | output_info->push_back(TapeTensorFromTensor(output_seq_array[i])); | ||||
2193 | if (PyErr_Occurred() != nullptr) { | ||||
2194 | return false; | ||||
2195 | } | ||||
2196 | } | ||||
2197 | return true; | ||||
2198 | } | ||||
2199 | |||||
2200 | std::vector<int64_t> MakeTensorIDList(PyObject* tensors) { | ||||
2201 | PyObject* seq = PySequence_Fast(tensors, "expected a sequence"); | ||||
2202 | if (seq == nullptr) { | ||||
2203 | return {}; | ||||
2204 | } | ||||
2205 | int len = PySequence_Fast_GET_SIZE(seq)(((((((PyObject*)(seq))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((static_cast<void> (0)), (((PyVarObject *)(seq))->ob_size)) : (((PyVarObject*)(((static_cast<void > (0)), (PyTupleObject *)(seq))))->ob_size)); | ||||
2206 | PyObject** seq_array = PySequence_Fast_ITEMS(seq)(((((((PyObject*)(seq))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((PyListObject *)(seq))->ob_item : ((PyTupleObject *)(seq))->ob_item); | ||||
2207 | std::vector<int64_t> list; | ||||
2208 | list.reserve(len); | ||||
2209 | for (int i = 0; i < len; ++i) { | ||||
2210 | PyObject* tensor = seq_array[i]; | ||||
2211 | list.push_back(FastTensorId(tensor)); | ||||
2212 | if (PyErr_Occurred()) { | ||||
2213 | Py_DECREF(seq)_Py_DECREF(((PyObject*)(seq))); | ||||
2214 | return list; | ||||
2215 | } | ||||
2216 | } | ||||
2217 | Py_DECREF(seq)_Py_DECREF(((PyObject*)(seq))); | ||||
2218 | return list; | ||||
2219 | } | ||||
2220 | |||||
2221 | void TFE_Py_TapeVariableAccessed(PyObject* variable) { | ||||
2222 | if (!CouldBackprop()) { | ||||
2223 | return; | ||||
2224 | } | ||||
2225 | for (TFE_Py_Tape* tape : SafeTapeSet()) { | ||||
2226 | tape->tape->VariableAccessed(variable); | ||||
2227 | } | ||||
2228 | } | ||||
2229 | |||||
2230 | void TFE_Py_TapeWatchVariable(PyObject* tape, PyObject* variable) { | ||||
2231 | if (!CouldBackprop()) { | ||||
2232 | return; | ||||
2233 | } | ||||
2234 | reinterpret_cast<TFE_Py_Tape*>(tape)->tape->WatchVariable(variable); | ||||
2235 | } | ||||
2236 | |||||
2237 | PyObject* TFE_Py_TapeWatchedVariables(PyObject* tape) { | ||||
2238 | return reinterpret_cast<TFE_Py_Tape*>(tape)->tape->GetVariablesAsPyTuple(); | ||||
2239 | } | ||||
2240 | |||||
2241 | PyObject* TFE_Py_VariableWatcherNew() { | ||||
2242 | TFE_Py_VariableWatcher_Type.tp_new = PyType_GenericNew; | ||||
2243 | if (PyType_Ready(&TFE_Py_VariableWatcher_Type) < 0) return nullptr; | ||||
2244 | TFE_Py_VariableWatcher* variable_watcher = | ||||
2245 | PyObject_NEW(TFE_Py_VariableWatcher, &TFE_Py_VariableWatcher_Type)( (TFE_Py_VariableWatcher *) PyObject_Init( (PyObject *) PyObject_Malloc ( ( (&TFE_Py_VariableWatcher_Type)->tp_basicsize ) ), ( &TFE_Py_VariableWatcher_Type)) ); | ||||
2246 | variable_watcher->variable_watcher = new VariableWatcher(); | ||||
2247 | Py_INCREF(variable_watcher)_Py_INCREF(((PyObject*)(variable_watcher))); | ||||
2248 | GetVariableWatcherSet()->insert(variable_watcher); | ||||
2249 | return reinterpret_cast<PyObject*>(variable_watcher); | ||||
2250 | } | ||||
2251 | |||||
2252 | void TFE_Py_VariableWatcherRemove(PyObject* variable_watcher) { | ||||
2253 | auto* stack = GetVariableWatcherSet(); | ||||
2254 | stack->erase(reinterpret_cast<TFE_Py_VariableWatcher*>(variable_watcher)); | ||||
2255 | // We kept a reference to the variable watcher in the set to ensure it | ||||
2256 | // wouldn't get deleted under us; cleaning it up here. | ||||
2257 | Py_DECREF(variable_watcher)_Py_DECREF(((PyObject*)(variable_watcher))); | ||||
2258 | } | ||||
2259 | |||||
2260 | void TFE_Py_VariableWatcherVariableAccessed(PyObject* variable) { | ||||
2261 | for (TFE_Py_VariableWatcher* variable_watcher : SafeVariableWatcherSet()) { | ||||
2262 | variable_watcher->variable_watcher->WatchVariable(variable); | ||||
2263 | } | ||||
2264 | } | ||||
2265 | |||||
2266 | PyObject* TFE_Py_VariableWatcherWatchedVariables(PyObject* variable_watcher) { | ||||
2267 | return reinterpret_cast<TFE_Py_VariableWatcher*>(variable_watcher) | ||||
2268 | ->variable_watcher->GetVariablesAsPyTuple(); | ||||
2269 | } | ||||
2270 | |||||
2271 | namespace { | ||||
2272 | std::vector<tensorflow::DataType> MakeTensorDtypeList(PyObject* tensors) { | ||||
2273 | PyObject* seq = PySequence_Fast(tensors, "expected a sequence"); | ||||
2274 | if (seq == nullptr) { | ||||
2275 | return {}; | ||||
2276 | } | ||||
2277 | int len = PySequence_Fast_GET_SIZE(seq)(((((((PyObject*)(seq))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((static_cast<void> (0)), (((PyVarObject *)(seq))->ob_size)) : (((PyVarObject*)(((static_cast<void > (0)), (PyTupleObject *)(seq))))->ob_size)); | ||||
2278 | PyObject** seq_array = PySequence_Fast_ITEMS(seq)(((((((PyObject*)(seq))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((PyListObject *)(seq))->ob_item : ((PyTupleObject *)(seq))->ob_item); | ||||
2279 | std::vector<tensorflow::DataType> list; | ||||
2280 | list.reserve(len); | ||||
2281 | for (int i = 0; i < len; ++i) { | ||||
2282 | PyObject* tensor = seq_array[i]; | ||||
2283 | list.push_back(tensorflow::PyTensor_DataType(tensor)); | ||||
2284 | } | ||||
2285 | Py_DECREF(seq)_Py_DECREF(((PyObject*)(seq))); | ||||
2286 | return list; | ||||
2287 | } | ||||
2288 | |||||
2289 | PyObject* ForwardAccumulatorDeleteGradient(PyObject* tensor_id, | ||||
2290 | PyObject* weak_tensor_ref) { | ||||
2291 | int64_t parsed_tensor_id = MakeInt(tensor_id); | ||||
2292 | for (TFE_Py_ForwardAccumulator* accumulator : *GetAccumulatorSet()) { | ||||
2293 | accumulator->accumulator->DeleteGradient(parsed_tensor_id); | ||||
2294 | } | ||||
2295 | Py_DECREF(weak_tensor_ref)_Py_DECREF(((PyObject*)(weak_tensor_ref))); | ||||
2296 | Py_DECREF(tensor_id)_Py_DECREF(((PyObject*)(tensor_id))); | ||||
2297 | Py_INCREF(Py_None)_Py_INCREF(((PyObject*)((&_Py_NoneStruct)))); | ||||
2298 | return Py_None(&_Py_NoneStruct); | ||||
2299 | } | ||||
2300 | |||||
2301 | static PyMethodDef forward_accumulator_delete_gradient_method_def = { | ||||
2302 | "ForwardAccumulatorDeleteGradient", ForwardAccumulatorDeleteGradient, | ||||
2303 | METH_O0x0008, "ForwardAccumulatorDeleteGradient"}; | ||||
2304 | |||||
2305 | void RegisterForwardAccumulatorCleanup(PyObject* tensor, int64_t tensor_id) { | ||||
2306 | tensorflow::Safe_PyObjectPtr callback( | ||||
2307 | PyCFunction_New(&forward_accumulator_delete_gradient_method_def,PyCFunction_NewEx((&forward_accumulator_delete_gradient_method_def ), (PyLong_FromLong(tensor_id)), __null) | ||||
2308 | PyLong_FromLong(tensor_id))PyCFunction_NewEx((&forward_accumulator_delete_gradient_method_def ), (PyLong_FromLong(tensor_id)), __null)); | ||||
2309 | // We need to keep a reference to the weakref active if we want our callback | ||||
2310 | // called. The callback itself now owns the weakref object and the tensor ID | ||||
2311 | // object. | ||||
2312 | PyWeakref_NewRef(tensor, callback.get()); | ||||
2313 | } | ||||
2314 | |||||
2315 | void TapeSetRecordBackprop( | ||||
2316 | const string& op_type, const std::vector<PyTapeTensor>& output_info, | ||||
2317 | const std::vector<int64_t>& input_ids, | ||||
2318 | const std::vector<tensorflow::DataType>& input_dtypes, | ||||
2319 | const std::function<PyBackwardFunction*()>& backward_function_getter, | ||||
2320 | const std::function<void(PyBackwardFunction*)>& backward_function_killer, | ||||
2321 | tensorflow::uint64 max_gradient_tape_id) { | ||||
2322 | if (!CouldBackprop()) { | ||||
2323 | return; | ||||
2324 | } | ||||
2325 | for (TFE_Py_Tape* tape : SafeTapeSet()) { | ||||
2326 | if (tape->nesting_id < max_gradient_tape_id) { | ||||
2327 | tape->tape->RecordOperation(op_type, output_info, input_ids, input_dtypes, | ||||
2328 | backward_function_getter, | ||||
2329 | backward_function_killer); | ||||
2330 | } | ||||
2331 | } | ||||
2332 | } | ||||
2333 | |||||
2334 | bool TapeSetRecordForwardprop( | ||||
2335 | const string& op_type, PyObject* output_seq, | ||||
2336 | const std::vector<PyTapeTensor>& output_info, PyObject* input_tensors, | ||||
2337 | const std::vector<int64_t>& input_ids, | ||||
2338 | const std::vector<tensorflow::DataType>& input_dtypes, | ||||
2339 | const std::function<PyBackwardFunction*()>& backward_function_getter, | ||||
2340 | const std::function<void(PyBackwardFunction*)>& backward_function_killer, | ||||
2341 | const tensorflow::eager::ForwardFunction<PyObject>* forward_function, | ||||
2342 | PyObject* forwardprop_output_indices, | ||||
2343 | tensorflow::uint64* max_gradient_tape_id) { | ||||
2344 | *max_gradient_tape_id = std::numeric_limits<tensorflow::uint64>::max(); | ||||
2345 | if (!CouldForwardprop()) { | ||||
2346 | return true; | ||||
2347 | } | ||||
2348 | auto accumulator_set = SafeAccumulatorSet(); | ||||
2349 | tensorflow::Safe_PyObjectPtr input_seq( | ||||
2350 | PySequence_Fast(input_tensors, "expected a sequence of tensors")); | ||||
2351 | if (input_seq == nullptr || PyErr_Occurred()) return false; | ||||
2352 | Py_ssize_t input_len = PySequence_Fast_GET_SIZE(input_seq.get())(((((((PyObject*)(input_seq.get()))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((static_cast<void> (0)), (((PyVarObject*)(input_seq.get()))->ob_size)) : ((( PyVarObject*)(((static_cast<void> (0)), (PyTupleObject * )(input_seq.get()))))->ob_size)); | ||||
2353 | PyObject** output_seq_array = PySequence_Fast_ITEMS(output_seq)(((((((PyObject*)(output_seq))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((PyListObject *)(output_seq))-> ob_item : ((PyTupleObject *)(output_seq))->ob_item); | ||||
2354 | for (int i = 0; i < output_info.size(); ++i) { | ||||
2355 | RegisterForwardAccumulatorCleanup(output_seq_array[i], | ||||
2356 | output_info[i].GetID()); | ||||
2357 | } | ||||
2358 | if (forwardprop_output_indices != nullptr && | ||||
2359 | forwardprop_output_indices != Py_None(&_Py_NoneStruct)) { | ||||
2360 | tensorflow::Safe_PyObjectPtr indices_fast(PySequence_Fast( | ||||
2361 | forwardprop_output_indices, "Expected a sequence of indices")); | ||||
2362 | if (indices_fast == nullptr || PyErr_Occurred()) { | ||||
2363 | return false; | ||||
2364 | } | ||||
2365 | if (PySequence_Fast_GET_SIZE(indices_fast.get())(((((((PyObject*)(indices_fast.get()))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((static_cast<void> (0)), (((PyVarObject*)(indices_fast.get()))->ob_size)) : ( ((PyVarObject*)(((static_cast<void> (0)), (PyTupleObject *)(indices_fast.get()))))->ob_size)) != | ||||
2366 | accumulator_set.size()) { | ||||
2367 | MaybeRaiseExceptionFromStatus( | ||||
2368 | tensorflow::errors::Internal( | ||||
2369 | "Accumulators were added or removed from the active set " | ||||
2370 | "between packing and unpacking."), | ||||
2371 | nullptr); | ||||
2372 | } | ||||
2373 | PyObject** indices_fast_array = PySequence_Fast_ITEMS(indices_fast.get())(((((((PyObject*)(indices_fast.get()))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((PyListObject *)(indices_fast .get()))->ob_item : ((PyTupleObject *)(indices_fast.get()) )->ob_item); | ||||
2374 | Py_ssize_t accumulator_index = 0; | ||||
2375 | for (AccumulatorSet::const_reverse_iterator it = accumulator_set.rbegin(); | ||||
2376 | it != accumulator_set.rend(); ++it, ++accumulator_index) { | ||||
2377 | tensorflow::Safe_PyObjectPtr jvp_index_seq( | ||||
2378 | PySequence_Fast(indices_fast_array[accumulator_index], | ||||
2379 | "Expected a sequence of jvp indices.")); | ||||
2380 | if (jvp_index_seq == nullptr || PyErr_Occurred()) { | ||||
2381 | return false; | ||||
2382 | } | ||||
2383 | Py_ssize_t num_jvps = PySequence_Fast_GET_SIZE(jvp_index_seq.get())(((((((PyObject*)(jvp_index_seq.get()))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((static_cast<void> (0)), (((PyVarObject*)(jvp_index_seq.get()))->ob_size)) : (((PyVarObject*)(((static_cast<void> (0)), (PyTupleObject *)(jvp_index_seq.get()))))->ob_size)); | ||||
2384 | PyObject** jvp_index_seq_array = | ||||
2385 | PySequence_Fast_ITEMS(jvp_index_seq.get())(((((((PyObject*)(jvp_index_seq.get()))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((PyListObject *)(jvp_index_seq .get()))->ob_item : ((PyTupleObject *)(jvp_index_seq.get() ))->ob_item); | ||||
2386 | for (Py_ssize_t jvp_index = 0; jvp_index < num_jvps; ++jvp_index) { | ||||
2387 | PyObject* tuple = jvp_index_seq_array[jvp_index]; | ||||
2388 | int64_t primal_tensor_id = | ||||
2389 | output_info[MakeInt(PyTuple_GetItem(tuple, 0))].GetID(); | ||||
2390 | (*it)->accumulator->Watch( | ||||
2391 | primal_tensor_id, | ||||
2392 | output_seq_array[MakeInt(PyTuple_GetItem(tuple, 1))]); | ||||
2393 | } | ||||
2394 | } | ||||
2395 | } else { | ||||
2396 | std::vector<PyTapeTensor> input_info; | ||||
2397 | input_info.reserve(input_len); | ||||
2398 | PyObject** input_seq_array = PySequence_Fast_ITEMS(input_seq.get())(((((((PyObject*)(input_seq.get()))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((PyListObject *)(input_seq .get()))->ob_item : ((PyTupleObject *)(input_seq.get()))-> ob_item); | ||||
2399 | for (Py_ssize_t i = 0; i < input_len; ++i) { | ||||
2400 | input_info.push_back(TapeTensorFromTensor(input_seq_array[i])); | ||||
2401 | } | ||||
2402 | for (TFE_Py_ForwardAccumulator* accumulator : accumulator_set) { | ||||
2403 | tensorflow::Status status = accumulator->accumulator->Accumulate( | ||||
2404 | op_type, input_info, output_info, input_ids, input_dtypes, | ||||
2405 | forward_function, backward_function_getter, backward_function_killer); | ||||
2406 | if (PyErr_Occurred()) return false; // Don't swallow Python exceptions. | ||||
2407 | if (MaybeRaiseExceptionFromStatus(status, nullptr)) { | ||||
2408 | return false; | ||||
2409 | } | ||||
2410 | if (accumulator->accumulator->BusyAccumulating()) { | ||||
2411 | // Ensure inner accumulators don't see outer accumulators' jvps. This | ||||
2412 | // mostly happens on its own, with some potentially surprising | ||||
2413 | // exceptions, so the blanket policy is for consistency. | ||||
2414 | *max_gradient_tape_id = accumulator->nesting_id; | ||||
2415 | break; | ||||
2416 | } | ||||
2417 | } | ||||
2418 | } | ||||
2419 | return true; | ||||
2420 | } | ||||
2421 | |||||
2422 | PyObject* TangentsAsPyTuple(const std::vector<PyObject*>& input_tangents) { | ||||
2423 | PyObject* py_input_tangents = PyTuple_New(input_tangents.size()); | ||||
2424 | for (int i = 0; i < input_tangents.size(); ++i) { | ||||
2425 | PyObject* element; | ||||
2426 | if (input_tangents[i] == nullptr) { | ||||
2427 | element = Py_None(&_Py_NoneStruct); | ||||
2428 | } else { | ||||
2429 | element = input_tangents[i]; | ||||
2430 | } | ||||
2431 | Py_INCREF(element)_Py_INCREF(((PyObject*)(element))); | ||||
2432 | PyTuple_SET_ITEM(py_input_tangents, i, element)PyTuple_SetItem(py_input_tangents, i, element); | ||||
2433 | } | ||||
2434 | return py_input_tangents; | ||||
2435 | } | ||||
2436 | |||||
2437 | tensorflow::Status ParseTangentOutputs( | ||||
2438 | PyObject* user_output, std::vector<PyObject*>* output_tangents) { | ||||
2439 | if (user_output == Py_None(&_Py_NoneStruct)) { | ||||
2440 | // No connected gradients. | ||||
2441 | return tensorflow::Status::OK(); | ||||
2442 | } | ||||
2443 | tensorflow::Safe_PyObjectPtr fast_result( | ||||
2444 | PySequence_Fast(user_output, "expected a sequence of forward gradients")); | ||||
2445 | if (fast_result == nullptr) { | ||||
2446 | return tensorflow::errors::InvalidArgument( | ||||
2447 | "forward gradient function did not return a sequence."); | ||||
2448 | } | ||||
2449 | int len = PySequence_Fast_GET_SIZE(fast_result.get())(((((((PyObject*)(fast_result.get()))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((static_cast<void> (0)), (((PyVarObject*)(fast_result.get()))->ob_size)) : ( ((PyVarObject*)(((static_cast<void> (0)), (PyTupleObject *)(fast_result.get()))))->ob_size)); | ||||
2450 | PyObject** fast_result_array = PySequence_Fast_ITEMS(fast_result.get())(((((((PyObject*)(fast_result.get()))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((PyListObject *)(fast_result .get()))->ob_item : ((PyTupleObject *)(fast_result.get())) ->ob_item); | ||||
2451 | output_tangents->reserve(len); | ||||
2452 | for (int i = 0; i < len; ++i) { | ||||
2453 | PyObject* item = fast_result_array[i]; | ||||
2454 | if (item == Py_None(&_Py_NoneStruct)) { | ||||
2455 | output_tangents->push_back(nullptr); | ||||
2456 | } else { | ||||
2457 | Py_INCREF(item)_Py_INCREF(((PyObject*)(item))); | ||||
2458 | output_tangents->push_back(item); | ||||
2459 | } | ||||
2460 | } | ||||
2461 | return tensorflow::Status::OK(); | ||||
2462 | } | ||||
2463 | |||||
2464 | // Calls the registered forward_gradient_function, computing `output_tangents` | ||||
2465 | // from `input_tangents`. `output_tangents` must not be null. | ||||
2466 | // | ||||
2467 | // `op_name`, `attrs`, `inputs`, and `results` describe the operation for which | ||||
2468 | // the forward function is being called. | ||||
2469 | tensorflow::Status CallJVPFunction(PyObject* op_name, PyObject* attrs, | ||||
2470 | PyObject* inputs, PyObject* results, | ||||
2471 | const std::vector<PyObject*>& input_tangents, | ||||
2472 | std::vector<PyObject*>* output_tangents, | ||||
2473 | bool use_batch) { | ||||
2474 | if (forward_gradient_function == nullptr) { | ||||
2475 | return tensorflow::errors::Internal( | ||||
2476 | "No forward gradient function registered."); | ||||
2477 | } | ||||
2478 | tensorflow::Safe_PyObjectPtr py_input_tangents( | ||||
2479 | TangentsAsPyTuple(input_tangents)); | ||||
2480 | |||||
2481 | // Normalize the input sequence to a tuple so it works with function | ||||
2482 | // caching; otherwise it may be an opaque _InputList object. | ||||
2483 | tensorflow::Safe_PyObjectPtr input_tuple(PySequence_Tuple(inputs)); | ||||
2484 | PyObject* to_batch = (use_batch) ? Py_True((PyObject *) &_Py_TrueStruct) : Py_False((PyObject *) &_Py_FalseStruct); | ||||
2485 | tensorflow::Safe_PyObjectPtr callback_args( | ||||
2486 | Py_BuildValue("OOOOOO", op_name, attrs, input_tuple.get(), results, | ||||
2487 | py_input_tangents.get(), to_batch)); | ||||
2488 | tensorflow::Safe_PyObjectPtr py_result( | ||||
2489 | PyObject_CallObject(forward_gradient_function, callback_args.get())); | ||||
2490 | if (py_result == nullptr || PyErr_Occurred()) { | ||||
2491 | return tensorflow::errors::Internal( | ||||
2492 | "forward gradient function threw exceptions"); | ||||
2493 | } | ||||
2494 | return ParseTangentOutputs(py_result.get(), output_tangents); | ||||
2495 | } | ||||
2496 | |||||
2497 | // Like CallJVPFunction, but calls a pre-bound forward function. | ||||
2498 | // These are passed in from a record_gradient argument. | ||||
2499 | tensorflow::Status CallOpSpecificJVPFunction( | ||||
2500 | PyObject* op_specific_forward_function, | ||||
2501 | const std::vector<PyObject*>& input_tangents, | ||||
2502 | std::vector<PyObject*>* output_tangents) { | ||||
2503 | tensorflow::Safe_PyObjectPtr py_input_tangents( | ||||
2504 | TangentsAsPyTuple(input_tangents)); | ||||
2505 | |||||
2506 | tensorflow::Safe_PyObjectPtr py_result(PyObject_CallObject( | ||||
2507 | op_specific_forward_function, py_input_tangents.get())); | ||||
2508 | if (py_result == nullptr || PyErr_Occurred()) { | ||||
2509 | return tensorflow::errors::Internal( | ||||
2510 | "forward gradient function threw exceptions"); | ||||
2511 | } | ||||
2512 | return ParseTangentOutputs(py_result.get(), output_tangents); | ||||
2513 | } | ||||
2514 | |||||
2515 | bool ParseOpTypeString(PyObject* op_type, string* op_type_string) { | ||||
2516 | if (PyBytes_Check(op_type)((((((PyObject*)(op_type))->ob_type))->tp_flags & ( (1UL << 27))) != 0)) { | ||||
2517 | *op_type_string = PyBytes_AsString(op_type); | ||||
2518 | } else if (PyUnicode_Check(op_type)((((((PyObject*)(op_type))->ob_type))->tp_flags & ( (1UL << 28))) != 0)) { | ||||
2519 | #if PY_MAJOR_VERSION3 >= 3 | ||||
2520 | *op_type_string = PyUnicode_AsUTF8(op_type); | ||||
2521 | #else | ||||
2522 | PyObject* py_str = PyUnicode_AsUTF8String(op_type); | ||||
2523 | if (py_str == nullptr) { | ||||
2524 | return false; | ||||
2525 | } | ||||
2526 | *op_type_string = PyBytes_AS_STRING(py_str)((static_cast<void> (0)), (((PyBytesObject *)(py_str))-> ob_sval)); | ||||
2527 | Py_DECREF(py_str)_Py_DECREF(((PyObject*)(py_str))); | ||||
2528 | #endif | ||||
2529 | } else { | ||||
2530 | PyErr_SetString(PyExc_RuntimeError, "op_type should be a string."); | ||||
2531 | return false; | ||||
2532 | } | ||||
2533 | return true; | ||||
2534 | } | ||||
2535 | |||||
2536 | bool TapeSetRecordOperation( | ||||
2537 | PyObject* op_type, PyObject* input_tensors, PyObject* output_tensors, | ||||
2538 | const std::vector<int64_t>& input_ids, | ||||
2539 | const std::vector<tensorflow::DataType>& input_dtypes, | ||||
2540 | const std::function<PyBackwardFunction*()>& backward_function_getter, | ||||
2541 | const std::function<void(PyBackwardFunction*)>& backward_function_killer, | ||||
2542 | const tensorflow::eager::ForwardFunction<PyObject>* forward_function) { | ||||
2543 | std::vector<PyTapeTensor> output_info; | ||||
2544 | tensorflow::Safe_PyObjectPtr output_seq(PySequence_Fast( | ||||
2545 | output_tensors, "expected a sequence of integer tensor ids")); | ||||
2546 | if (PyErr_Occurred() || | ||||
2547 | !TapeTensorsFromTensorSequence(output_seq.get(), &output_info)) { | ||||
2548 | return false; | ||||
2549 | } | ||||
2550 | string op_type_str; | ||||
2551 | if (!ParseOpTypeString(op_type, &op_type_str)) { | ||||
2552 | return false; | ||||
2553 | } | ||||
2554 | tensorflow::uint64 max_gradient_tape_id; | ||||
2555 | if (!TapeSetRecordForwardprop( | ||||
2556 | op_type_str, output_seq.get(), output_info, input_tensors, input_ids, | ||||
2557 | input_dtypes, backward_function_getter, backward_function_killer, | ||||
2558 | forward_function, nullptr /* No special-cased jvps. */, | ||||
2559 | &max_gradient_tape_id)) { | ||||
2560 | return false; | ||||
2561 | } | ||||
2562 | TapeSetRecordBackprop(op_type_str, output_info, input_ids, input_dtypes, | ||||
2563 | backward_function_getter, backward_function_killer, | ||||
2564 | max_gradient_tape_id); | ||||
2565 | return true; | ||||
2566 | } | ||||
2567 | } // namespace | ||||
2568 | |||||
2569 | PyObject* TFE_Py_TapeSetRecordOperation(PyObject* op_type, | ||||
2570 | PyObject* output_tensors, | ||||
2571 | PyObject* input_tensors, | ||||
2572 | PyObject* backward_function, | ||||
2573 | PyObject* forward_function) { | ||||
2574 | if (!HasAccumulatorOrTape() || *ThreadTapeIsStopped()) { | ||||
2575 | Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (& _Py_NoneStruct); | ||||
2576 | } | ||||
2577 | std::vector<int64_t> input_ids = MakeTensorIDList(input_tensors); | ||||
2578 | if (PyErr_Occurred()) return nullptr; | ||||
2579 | |||||
2580 | std::vector<tensorflow::DataType> input_dtypes = | ||||
2581 | MakeTensorDtypeList(input_tensors); | ||||
2582 | if (PyErr_Occurred()) return nullptr; | ||||
2583 | |||||
2584 | std::function<PyBackwardFunction*()> backward_function_getter( | ||||
2585 | [backward_function]() { | ||||
2586 | Py_INCREF(backward_function)_Py_INCREF(((PyObject*)(backward_function))); | ||||
2587 | PyBackwardFunction* function = new PyBackwardFunction( | ||||
2588 | [backward_function](PyObject* out_grads, | ||||
2589 | const std::vector<int64_t>& unused) { | ||||
2590 | return PyObject_CallObject(backward_function, out_grads); | ||||
2591 | }); | ||||
2592 | return function; | ||||
2593 | }); | ||||
2594 | std::function<void(PyBackwardFunction*)> backward_function_killer( | ||||
2595 | [backward_function](PyBackwardFunction* py_backward_function) { | ||||
2596 | Py_DECREF(backward_function)_Py_DECREF(((PyObject*)(backward_function))); | ||||
2597 | delete py_backward_function; | ||||
2598 | }); | ||||
2599 | |||||
2600 | if (forward_function == Py_None(&_Py_NoneStruct)) { | ||||
2601 | if (!TapeSetRecordOperation( | ||||
2602 | op_type, input_tensors, output_tensors, input_ids, input_dtypes, | ||||
2603 | backward_function_getter, backward_function_killer, | ||||
2604 | nullptr /* No special-cased forward function */)) { | ||||
2605 | return nullptr; | ||||
2606 | } | ||||
2607 | } else { | ||||
2608 | tensorflow::eager::ForwardFunction<PyObject> wrapped_forward_function( | ||||
2609 | [forward_function](const std::vector<PyObject*>& input_tangents, | ||||
2610 | std::vector<PyObject*>* output_tangents, | ||||
2611 | bool use_batch = false) { | ||||
2612 | return CallOpSpecificJVPFunction(forward_function, input_tangents, | ||||
2613 | output_tangents); | ||||
2614 | }); | ||||
2615 | if (!TapeSetRecordOperation( | ||||
2616 | op_type, input_tensors, output_tensors, input_ids, input_dtypes, | ||||
2617 | backward_function_getter, backward_function_killer, | ||||
2618 | &wrapped_forward_function)) { | ||||
2619 | return nullptr; | ||||
2620 | } | ||||
2621 | } | ||||
2622 | Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (& _Py_NoneStruct); | ||||
2623 | } | ||||
2624 | |||||
2625 | PyObject* TFE_Py_TapeSetRecordOperationForwardprop( | ||||
2626 | PyObject* op_type, PyObject* output_tensors, PyObject* input_tensors, | ||||
2627 | PyObject* backward_function, PyObject* forwardprop_output_indices) { | ||||
2628 | if (!HasAccumulator() || *ThreadTapeIsStopped()) { | ||||
2629 | Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (& _Py_NoneStruct); | ||||
2630 | } | ||||
2631 | std::vector<int64_t> input_ids = MakeTensorIDList(input_tensors); | ||||
2632 | if (PyErr_Occurred()) return nullptr; | ||||
2633 | |||||
2634 | std::vector<tensorflow::DataType> input_dtypes = | ||||
2635 | MakeTensorDtypeList(input_tensors); | ||||
2636 | if (PyErr_Occurred()) return nullptr; | ||||
2637 | |||||
2638 | std::function<PyBackwardFunction*()> backward_function_getter( | ||||
2639 | [backward_function]() { | ||||
2640 | Py_INCREF(backward_function)_Py_INCREF(((PyObject*)(backward_function))); | ||||
2641 | PyBackwardFunction* function = new PyBackwardFunction( | ||||
2642 | [backward_function](PyObject* out_grads, | ||||
2643 | const std::vector<int64_t>& unused) { | ||||
2644 | return PyObject_CallObject(backward_function, out_grads); | ||||
2645 | }); | ||||
2646 | return function; | ||||
2647 | }); | ||||
2648 | std::function<void(PyBackwardFunction*)> backward_function_killer( | ||||
2649 | [backward_function](PyBackwardFunction* py_backward_function) { | ||||
2650 | Py_DECREF(backward_function)_Py_DECREF(((PyObject*)(backward_function))); | ||||
2651 | delete py_backward_function; | ||||
2652 | }); | ||||
2653 | std::vector<PyTapeTensor> output_info; | ||||
2654 | tensorflow::Safe_PyObjectPtr output_seq(PySequence_Fast( | ||||
2655 | output_tensors, "expected a sequence of integer tensor ids")); | ||||
2656 | if (PyErr_Occurred() || | ||||
2657 | !TapeTensorsFromTensorSequence(output_seq.get(), &output_info)) { | ||||
2658 | return nullptr; | ||||
2659 | } | ||||
2660 | string op_type_str; | ||||
2661 | if (!ParseOpTypeString(op_type, &op_type_str)) { | ||||
2662 | return nullptr; | ||||
2663 | } | ||||
2664 | tensorflow::uint64 max_gradient_tape_id; | ||||
2665 | if (!TapeSetRecordForwardprop( | ||||
2666 | op_type_str, output_seq.get(), output_info, input_tensors, input_ids, | ||||
2667 | input_dtypes, backward_function_getter, backward_function_killer, | ||||
2668 | nullptr /* no special-cased forward function */, | ||||
2669 | forwardprop_output_indices, &max_gradient_tape_id)) { | ||||
2670 | return nullptr; | ||||
2671 | } | ||||
2672 | Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (& _Py_NoneStruct); | ||||
2673 | } | ||||
2674 | |||||
2675 | PyObject* TFE_Py_TapeSetRecordOperationBackprop(PyObject* op_type, | ||||
2676 | PyObject* output_tensors, | ||||
2677 | PyObject* input_tensors, | ||||
2678 | PyObject* backward_function) { | ||||
2679 | if (!CouldBackprop()) { | ||||
2680 | Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (& _Py_NoneStruct); | ||||
2681 | } | ||||
2682 | std::vector<int64_t> input_ids = MakeTensorIDList(input_tensors); | ||||
2683 | if (PyErr_Occurred()) return nullptr; | ||||
2684 | |||||
2685 | std::vector<tensorflow::DataType> input_dtypes = | ||||
2686 | MakeTensorDtypeList(input_tensors); | ||||
2687 | if (PyErr_Occurred()) return nullptr; | ||||
2688 | |||||
2689 | std::function<PyBackwardFunction*()> backward_function_getter( | ||||
2690 | [backward_function]() { | ||||
2691 | Py_INCREF(backward_function)_Py_INCREF(((PyObject*)(backward_function))); | ||||
2692 | PyBackwardFunction* function = new PyBackwardFunction( | ||||
2693 | [backward_function](PyObject* out_grads, | ||||
2694 | const std::vector<int64_t>& unused) { | ||||
2695 | return PyObject_CallObject(backward_function, out_grads); | ||||
2696 | }); | ||||
2697 | return function; | ||||
2698 | }); | ||||
2699 | std::function<void(PyBackwardFunction*)> backward_function_killer( | ||||
2700 | [backward_function](PyBackwardFunction* py_backward_function) { | ||||
2701 | Py_DECREF(backward_function)_Py_DECREF(((PyObject*)(backward_function))); | ||||
2702 | delete py_backward_function; | ||||
2703 | }); | ||||
2704 | std::vector<PyTapeTensor> output_info; | ||||
2705 | tensorflow::Safe_PyObjectPtr output_seq(PySequence_Fast( | ||||
2706 | output_tensors, "expected a sequence of integer tensor ids")); | ||||
2707 | if (PyErr_Occurred() || | ||||
2708 | !TapeTensorsFromTensorSequence(output_seq.get(), &output_info)) { | ||||
2709 | return nullptr; | ||||
2710 | } | ||||
2711 | string op_type_str; | ||||
2712 | if (!ParseOpTypeString(op_type, &op_type_str)) { | ||||
2713 | return nullptr; | ||||
2714 | } | ||||
2715 | TapeSetRecordBackprop(op_type_str, output_info, input_ids, input_dtypes, | ||||
2716 | backward_function_getter, backward_function_killer, | ||||
2717 | // No filtering based on relative ordering with forward | ||||
2718 | // accumulators. | ||||
2719 | std::numeric_limits<tensorflow::uint64>::max()); | ||||
2720 | Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (& _Py_NoneStruct); | ||||
2721 | } | ||||
2722 | |||||
2723 | void TFE_Py_TapeSetDeleteTrace(int64_t tensor_id) { | ||||
2724 | for (TFE_Py_Tape* tape : *GetTapeSet()) { | ||||
2725 | tape->tape->DeleteTrace(tensor_id); | ||||
2726 | } | ||||
2727 | } | ||||
2728 | |||||
2729 | std::vector<PyObject*> MakeTensorList(PyObject* tensors) { | ||||
2730 | PyObject* seq = PySequence_Fast(tensors, "expected a sequence"); | ||||
2731 | if (seq == nullptr) { | ||||
2732 | return {}; | ||||
2733 | } | ||||
2734 | int len = PySequence_Fast_GET_SIZE(seq)(((((((PyObject*)(seq))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((static_cast<void> (0)), (((PyVarObject *)(seq))->ob_size)) : (((PyVarObject*)(((static_cast<void > (0)), (PyTupleObject *)(seq))))->ob_size)); | ||||
2735 | PyObject** seq_array = PySequence_Fast_ITEMS(seq)(((((((PyObject*)(seq))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((PyListObject *)(seq))->ob_item : ((PyTupleObject *)(seq))->ob_item); | ||||
2736 | std::vector<PyObject*> list(seq_array, seq_array + len); | ||||
2737 | Py_DECREF(seq)_Py_DECREF(((PyObject*)(seq))); | ||||
2738 | return list; | ||||
2739 | } | ||||
2740 | |||||
2741 | PyObject* TFE_Py_TapeGradient(PyObject* tape, PyObject* target, | ||||
2742 | PyObject* sources, PyObject* output_gradients, | ||||
2743 | PyObject* sources_raw, | ||||
2744 | PyObject* unconnected_gradients, | ||||
2745 | TF_Status* status) { | ||||
2746 | TFE_Py_Tape* tape_obj = reinterpret_cast<TFE_Py_Tape*>(tape); | ||||
2747 | if (!tape_obj->tape->IsPersistent()) { | ||||
2748 | auto* tape_set = GetTapeSet(); | ||||
2749 | if (tape_set->find(tape_obj) != tape_set->end()) { | ||||
2750 | PyErr_SetString(PyExc_RuntimeError, | ||||
2751 | "gradient() cannot be invoked within the " | ||||
2752 | "GradientTape context (i.e., while operations are being " | ||||
2753 | "recorded). Either move the call to gradient() to be " | ||||
2754 | "outside the 'with tf.GradientTape' block, or " | ||||
2755 | "use a persistent tape: " | ||||
2756 | "'with tf.GradientTape(persistent=true)'"); | ||||
2757 | return nullptr; | ||||
2758 | } | ||||
2759 | } | ||||
2760 | |||||
2761 | std::vector<int64_t> target_vec = MakeTensorIDList(target); | ||||
2762 | if (PyErr_Occurred()) { | ||||
2763 | return nullptr; | ||||
2764 | } | ||||
2765 | std::vector<int64_t> sources_vec = MakeTensorIDList(sources); | ||||
2766 | if (PyErr_Occurred()) { | ||||
2767 | return nullptr; | ||||
2768 | } | ||||
2769 | tensorflow::gtl::FlatSet<int64_t> sources_set(sources_vec.begin(), | ||||
2770 | sources_vec.end()); | ||||
2771 | |||||
2772 | tensorflow::Safe_PyObjectPtr seq = | ||||
2773 | tensorflow::make_safe(PySequence_Fast(target, "expected a sequence")); | ||||
2774 | int len = PySequence_Fast_GET_SIZE(seq.get())(((((((PyObject*)(seq.get()))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((static_cast<void> (0)), (((PyVarObject*)(seq.get()))->ob_size)) : (((PyVarObject* )(((static_cast<void> (0)), (PyTupleObject *)(seq.get() ))))->ob_size)); | ||||
2775 | PyObject** seq_array = PySequence_Fast_ITEMS(seq.get())(((((((PyObject*)(seq.get()))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((PyListObject *)(seq.get()))-> ob_item : ((PyTupleObject *)(seq.get()))->ob_item); | ||||
2776 | std::unordered_map<int64_t, PyTapeTensor> source_tensors_that_are_targets; | ||||
2777 | for (int i = 0; i < len; ++i) { | ||||
2778 | int64_t target_id = target_vec[i]; | ||||
2779 | if (sources_set.find(target_id) != sources_set.end()) { | ||||
2780 | auto tensor = seq_array[i]; | ||||
2781 | source_tensors_that_are_targets.insert( | ||||
2782 | std::make_pair(target_id, TapeTensorFromTensor(tensor))); | ||||
2783 | } | ||||
2784 | if (PyErr_Occurred()) { | ||||
2785 | return nullptr; | ||||
2786 | } | ||||
2787 | } | ||||
2788 | if (PyErr_Occurred()) { | ||||
2789 | return nullptr; | ||||
2790 | } | ||||
2791 | |||||
2792 | std::vector<PyObject*> outgrad_vec; | ||||
2793 | if (output_gradients != Py_None(&_Py_NoneStruct)) { | ||||
2794 | outgrad_vec = MakeTensorList(output_gradients); | ||||
2795 | if (PyErr_Occurred()) { | ||||
2796 | return nullptr; | ||||
2797 | } | ||||
2798 | for (PyObject* tensor : outgrad_vec) { | ||||
2799 | // Calling the backward function will eat a reference to the tensors in | ||||
2800 | // outgrad_vec, so we need to increase their reference count. | ||||
2801 | Py_INCREF(tensor)_Py_INCREF(((PyObject*)(tensor))); | ||||
2802 | } | ||||
2803 | } | ||||
2804 | std::vector<PyObject*> result(sources_vec.size()); | ||||
2805 | status->status = tape_obj->tape->ComputeGradient( | ||||
2806 | *py_vspace, target_vec, sources_vec, source_tensors_that_are_targets, | ||||
2807 | outgrad_vec, absl::MakeSpan(result)); | ||||
2808 | if (!status->status.ok()) { | ||||
2809 | if (PyErr_Occurred()) { | ||||
2810 | // Do not propagate the erroneous status as that would swallow the | ||||
2811 | // exception which caused the problem. | ||||
2812 | status->status = tensorflow::Status::OK(); | ||||
2813 | } | ||||
2814 | return nullptr; | ||||
2815 | } | ||||
2816 | |||||
2817 | bool unconnected_gradients_zero = | ||||
2818 | strcmp(TFE_GetPythonString(unconnected_gradients), "zero") == 0; | ||||
2819 | std::vector<PyObject*> sources_obj; | ||||
2820 | if (unconnected_gradients_zero) { | ||||
2821 | // Uses the "raw" sources here so it can properly make a zeros tensor even | ||||
2822 | // if there are resource variables as sources. | ||||
2823 | sources_obj = MakeTensorList(sources_raw); | ||||
2824 | } | ||||
2825 | |||||
2826 | if (!result.empty()) { | ||||
2827 | PyObject* py_result = PyList_New(result.size()); | ||||
2828 | tensorflow::gtl::FlatSet<PyObject*> seen_results(result.size()); | ||||
2829 | for (int i = 0; i < result.size(); ++i) { | ||||
2830 | if (result[i] == nullptr) { | ||||
2831 | if (unconnected_gradients_zero) { | ||||
2832 | // generate a zeros tensor in the shape of sources[i] | ||||
2833 | tensorflow::DataType dtype = | ||||
2834 | tensorflow::PyTensor_DataType(sources_obj[i]); | ||||
2835 | PyTapeTensor tensor = | ||||
2836 | PyTapeTensor(sources_vec[i], dtype, sources_obj[i]); | ||||
2837 | result[i] = tensor.ZerosLike(); | ||||
2838 | } else { | ||||
2839 | Py_INCREF(Py_None)_Py_INCREF(((PyObject*)((&_Py_NoneStruct)))); | ||||
2840 | result[i] = Py_None(&_Py_NoneStruct); | ||||
2841 | } | ||||
2842 | } else if (seen_results.find(result[i]) != seen_results.end()) { | ||||
2843 | Py_INCREF(result[i])_Py_INCREF(((PyObject*)(result[i]))); | ||||
2844 | } | ||||
2845 | seen_results.insert(result[i]); | ||||
2846 | PyList_SET_ITEM(py_result, i, reinterpret_cast<PyObject*>(result[i]))PyList_SetItem(py_result, i, reinterpret_cast<PyObject*> (result[i])); | ||||
2847 | } | ||||
2848 | return py_result; | ||||
2849 | } | ||||
2850 | return PyList_New(0); | ||||
2851 | } | ||||
2852 | |||||
2853 | PyObject* TFE_Py_ForwardAccumulatorNew(bool use_batch) { | ||||
2854 | TFE_Py_ForwardAccumulator_Type.tp_new = PyType_GenericNew; | ||||
2855 | if (PyType_Ready(&TFE_Py_ForwardAccumulator_Type) < 0) return nullptr; | ||||
2856 | TFE_Py_ForwardAccumulator* accumulator = | ||||
2857 | PyObject_NEW(TFE_Py_ForwardAccumulator, &TFE_Py_ForwardAccumulator_Type)( (TFE_Py_ForwardAccumulator *) PyObject_Init( (PyObject *) PyObject_Malloc ( ( (&TFE_Py_ForwardAccumulator_Type)->tp_basicsize ) ) , (&TFE_Py_ForwardAccumulator_Type)) ); | ||||
2858 | if (py_vspace == nullptr) { | ||||
2859 | MaybeRaiseExceptionFromStatus( | ||||
2860 | tensorflow::errors::Internal( | ||||
2861 | "ForwardAccumulator requires a PyVSpace to be registered."), | ||||
2862 | nullptr); | ||||
2863 | } | ||||
2864 | accumulator->accumulator = new ForwardAccumulator(*py_vspace, use_batch); | ||||
2865 | return reinterpret_cast<PyObject*>(accumulator); | ||||
2866 | } | ||||
2867 | |||||
2868 | PyObject* TFE_Py_ForwardAccumulatorSetAdd(PyObject* accumulator) { | ||||
2869 | TFE_Py_ForwardAccumulator* c_accumulator( | ||||
2870 | reinterpret_cast<TFE_Py_ForwardAccumulator*>(accumulator)); | ||||
2871 | c_accumulator->nesting_id = tape_nesting_id_counter.fetch_add(1); | ||||
2872 | if (GetAccumulatorSet()->insert(c_accumulator)) { | ||||
2873 | Py_INCREF(accumulator)_Py_INCREF(((PyObject*)(accumulator))); | ||||
2874 | Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (& _Py_NoneStruct); | ||||
2875 | } else { | ||||
2876 | MaybeRaiseExceptionFromStatus( | ||||
2877 | tensorflow::errors::Internal( | ||||
2878 | "A ForwardAccumulator was added to the active set twice."), | ||||
2879 | nullptr); | ||||
2880 | return nullptr; | ||||
2881 | } | ||||
2882 | } | ||||
2883 | |||||
2884 | void TFE_Py_ForwardAccumulatorSetRemove(PyObject* accumulator) { | ||||
2885 | GetAccumulatorSet()->erase( | ||||
2886 | reinterpret_cast<TFE_Py_ForwardAccumulator*>(accumulator)); | ||||
2887 | Py_DECREF(accumulator)_Py_DECREF(((PyObject*)(accumulator))); | ||||
2888 | } | ||||
2889 | |||||
2890 | void TFE_Py_ForwardAccumulatorWatch(PyObject* accumulator, PyObject* tensor, | ||||
2891 | PyObject* tangent) { | ||||
2892 | int64_t tensor_id = FastTensorId(tensor); | ||||
2893 | reinterpret_cast<TFE_Py_ForwardAccumulator*>(accumulator) | ||||
2894 | ->accumulator->Watch(tensor_id, tangent); | ||||
2895 | RegisterForwardAccumulatorCleanup(tensor, tensor_id); | ||||
2896 | } | ||||
2897 | |||||
2898 | // Returns a new reference to the JVP Tensor. | ||||
2899 | PyObject* TFE_Py_ForwardAccumulatorJVP(PyObject* accumulator, | ||||
2900 | PyObject* tensor) { | ||||
2901 | PyObject* jvp = reinterpret_cast<TFE_Py_ForwardAccumulator*>(accumulator) | ||||
2902 | ->accumulator->FetchJVP(FastTensorId(tensor)); | ||||
2903 | if (jvp == nullptr) { | ||||
2904 | jvp = Py_None(&_Py_NoneStruct); | ||||
2905 | } | ||||
2906 | Py_INCREF(jvp)_Py_INCREF(((PyObject*)(jvp))); | ||||
2907 | return jvp; | ||||
2908 | } | ||||
2909 | |||||
2910 | PyObject* TFE_Py_PackJVPs(PyObject* tensors) { | ||||
2911 | if (!TapeCouldPossiblyRecord(tensors)) { | ||||
2912 | tensorflow::Safe_PyObjectPtr empty_tuple(PyTuple_New(0)); | ||||
2913 | tensorflow::Safe_PyObjectPtr empty_list(PyList_New(0)); | ||||
2914 | return PyTuple_Pack(2, empty_tuple.get(), empty_list.get()); | ||||
2915 | } | ||||
2916 | auto accumulators = *GetAccumulatorSet(); | ||||
2917 | tensorflow::Safe_PyObjectPtr tensors_fast( | ||||
2918 | PySequence_Fast(tensors, "Expected a sequence of input Tensors.")); | ||||
2919 | if (tensors_fast == nullptr || PyErr_Occurred()) { | ||||
2920 | return nullptr; | ||||
2921 | } | ||||
2922 | std::vector<int64_t> augmented_input_ids; | ||||
2923 | int len = PySequence_Fast_GET_SIZE(tensors_fast.get())(((((((PyObject*)(tensors_fast.get()))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((static_cast<void> (0)), (((PyVarObject*)(tensors_fast.get()))->ob_size)) : ( ((PyVarObject*)(((static_cast<void> (0)), (PyTupleObject *)(tensors_fast.get()))))->ob_size)); | ||||
2924 | PyObject** tensors_fast_array = PySequence_Fast_ITEMS(tensors_fast.get())(((((((PyObject*)(tensors_fast.get()))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((PyListObject *)(tensors_fast .get()))->ob_item : ((PyTupleObject *)(tensors_fast.get()) )->ob_item); | ||||
2925 | for (Py_ssize_t position = 0; position < len; ++position) { | ||||
2926 | PyObject* input = tensors_fast_array[position]; | ||||
2927 | if (input == Py_None(&_Py_NoneStruct)) { | ||||
2928 | continue; | ||||
2929 | } | ||||
2930 | tensorflow::DataType input_dtype(tensorflow::PyTensor_DataType(input)); | ||||
2931 | if (input_dtype == tensorflow::DT_INVALID) { | ||||
2932 | return nullptr; | ||||
2933 | } | ||||
2934 | augmented_input_ids.push_back(FastTensorId(input)); | ||||
2935 | } | ||||
2936 | if (PyErr_Occurred()) { | ||||
2937 | return nullptr; | ||||
2938 | } | ||||
2939 | // Find the innermost accumulator such that all outer accumulators are | ||||
2940 | // recording. Any more deeply nested accumulators will not have their JVPs | ||||
2941 | // saved. | ||||
2942 | AccumulatorSet::const_iterator innermost_all_recording = accumulators.begin(); | ||||
2943 | for (; innermost_all_recording != accumulators.end(); | ||||
2944 | ++innermost_all_recording) { | ||||
2945 | if ((*innermost_all_recording)->accumulator->BusyAccumulating()) { | ||||
2946 | break; | ||||
2947 | } | ||||
2948 | } | ||||
2949 | AccumulatorSet::const_reverse_iterator reverse_innermost_all_recording( | ||||
2950 | innermost_all_recording); | ||||
2951 | |||||
2952 | bool saving_jvps = false; | ||||
2953 | tensorflow::Safe_PyObjectPtr all_indices(PyTuple_New(accumulators.size())); | ||||
2954 | std::vector<PyObject*> new_tensors; | ||||
2955 | Py_ssize_t accumulator_index = 0; | ||||
2956 | // Start with the innermost accumulators to give outer accumulators a chance | ||||
2957 | // to find their higher-order JVPs. | ||||
2958 | for (AccumulatorSet::const_reverse_iterator it = accumulators.rbegin(); | ||||
2959 | it != accumulators.rend(); ++it, ++accumulator_index) { | ||||
2960 | std::vector<int64_t> new_input_ids; | ||||
2961 | std::vector<std::pair<int64_t, int64_t>> accumulator_indices; | ||||
2962 | if (it == reverse_innermost_all_recording) { | ||||
2963 | saving_jvps = true; | ||||
2964 | } | ||||
2965 | if (saving_jvps) { | ||||
2966 | for (int input_index = 0; input_index < augmented_input_ids.size(); | ||||
2967 | ++input_index) { | ||||
2968 | int64_t existing_input = augmented_input_ids[input_index]; | ||||
2969 | PyObject* jvp = (*it)->accumulator->FetchJVP(existing_input); | ||||
2970 | if (jvp != nullptr) { | ||||
2971 | new_tensors.push_back(jvp); | ||||
2972 | new_input_ids.push_back(FastTensorId(jvp)); | ||||
2973 | accumulator_indices.emplace_back( | ||||
2974 | input_index, | ||||
2975 | augmented_input_ids.size() + new_input_ids.size() - 1); | ||||
2976 | } | ||||
2977 | } | ||||
2978 | } | ||||
2979 | tensorflow::Safe_PyObjectPtr accumulator_indices_py( | ||||
2980 | PyTuple_New(accumulator_indices.size())); | ||||
2981 | for (int i = 0; i < accumulator_indices.size(); ++i) { | ||||
2982 | tensorflow::Safe_PyObjectPtr from_index( | ||||
2983 | GetPythonObjectFromInt(accumulator_indices[i].first)); | ||||
2984 | tensorflow::Safe_PyObjectPtr to_index( | ||||
2985 | GetPythonObjectFromInt(accumulator_indices[i].second)); | ||||
2986 | PyTuple_SetItem(accumulator_indices_py.get(), i, | ||||
2987 | PyTuple_Pack(2, from_index.get(), to_index.get())); | ||||
2988 | } | ||||
2989 | PyTuple_SetItem(all_indices.get(), accumulator_index, | ||||
2990 | accumulator_indices_py.release()); | ||||
2991 | augmented_input_ids.insert(augmented_input_ids.end(), new_input_ids.begin(), | ||||
2992 | new_input_ids.end()); | ||||
2993 | } | ||||
2994 | |||||
2995 | tensorflow::Safe_PyObjectPtr new_tensors_py(PyList_New(new_tensors.size())); | ||||
2996 | for (int i = 0; i < new_tensors.size(); ++i) { | ||||
2997 | PyObject* jvp = new_tensors[i]; | ||||
2998 | Py_INCREF(jvp)_Py_INCREF(((PyObject*)(jvp))); | ||||
2999 | PyList_SET_ITEM(new_tensors_py.get(), i, jvp)PyList_SetItem(new_tensors_py.get(), i, jvp); | ||||
3000 | } | ||||
3001 | return PyTuple_Pack(2, all_indices.get(), new_tensors_py.get()); | ||||
3002 | } | ||||
3003 | |||||
3004 | namespace { | ||||
3005 | |||||
3006 | // Indices for the "args" tuple that's passed to TFE_Py_FastPathExecute_C. | ||||
3007 | enum FastPathExecuteArgIndex { | ||||
3008 | FAST_PATH_EXECUTE_ARG_CONTEXT = 0, | ||||
3009 | FAST_PATH_EXECUTE_ARG_OP_NAME = 1, | ||||
3010 | FAST_PATH_EXECUTE_ARG_NAME = 2, | ||||
3011 | FAST_PATH_EXECUTE_ARG_INPUT_START = 3 | ||||
3012 | }; | ||||
3013 | |||||
3014 | PyObject* GetPythonObjectFromString(tensorflow::StringPiece s) { | ||||
3015 | #if PY_MAJOR_VERSION3 >= 3 | ||||
3016 | return PyUnicode_FromStringAndSize(s.data(), s.size()); | ||||
3017 | #else | ||||
3018 | return PyBytes_FromStringAndSize(s.data(), s.size()); | ||||
3019 | #endif | ||||
3020 | } | ||||
3021 | |||||
3022 | bool CheckResourceVariable(PyObject* item) { | ||||
3023 | if (tensorflow::swig::IsResourceVariable(item)) { | ||||
3024 | tensorflow::Safe_PyObjectPtr handle( | ||||
3025 | PyObject_GetAttrString(item, "_handle")); | ||||
3026 | return EagerTensor_CheckExact(handle.get()); | ||||
3027 | } | ||||
3028 | |||||
3029 | return false; | ||||
3030 | } | ||||
3031 | |||||
3032 | bool IsNumberType(PyObject* item) { | ||||
3033 | #if PY_MAJOR_VERSION3 >= 3 | ||||
3034 | return PyFloat_Check(item)((((PyObject*)(item))->ob_type) == (&PyFloat_Type) || PyType_IsSubtype ((((PyObject*)(item))->ob_type), (&PyFloat_Type))) || PyLong_Check(item)((((((PyObject*)(item))->ob_type))->tp_flags & ((1UL << 24))) != 0); | ||||
3035 | #else | ||||
3036 | return PyFloat_Check(item)((((PyObject*)(item))->ob_type) == (&PyFloat_Type) || PyType_IsSubtype ((((PyObject*)(item))->ob_type), (&PyFloat_Type))) || PyInt_Check(item) || PyLong_Check(item)((((((PyObject*)(item))->ob_type))->tp_flags & ((1UL << 24))) != 0); | ||||
3037 | #endif | ||||
3038 | } | ||||
3039 | |||||
3040 | bool CheckOneInput(PyObject* item) { | ||||
3041 | if (EagerTensor_CheckExact(item) || CheckResourceVariable(item) || | ||||
3042 | PyArray_Check(item)((((PyObject*)(item))->ob_type) == (&(*(PyTypeObject * )_tensorflow_numpy_api[2])) || PyType_IsSubtype((((PyObject*) (item))->ob_type), (&(*(PyTypeObject *)_tensorflow_numpy_api [2])))) || IsNumberType(item)) { | ||||
3043 | return true; | ||||
3044 | } | ||||
3045 | |||||
3046 | // Sequences are not properly handled. Sequences with purely python numeric | ||||
3047 | // types work, but sequences with mixes of EagerTensors and python numeric | ||||
3048 | // types don't work. | ||||
3049 | // TODO(nareshmodi): fix | ||||
3050 | return false; | ||||
3051 | } | ||||
3052 | |||||
3053 | bool CheckInputsOk(PyObject* seq, int start_index, | ||||
3054 | const tensorflow::OpDef& op_def) { | ||||
3055 | for (int i = 0; i < op_def.input_arg_size(); i++) { | ||||
3056 | PyObject* item = PyTuple_GET_ITEM(seq, i + start_index)(((static_cast<void> (0)), (PyTupleObject *)(seq))-> ob_item[i + start_index]); | ||||
3057 | if (!op_def.input_arg(i).number_attr().empty() || | ||||
3058 | !op_def.input_arg(i).type_list_attr().empty()) { | ||||
3059 | // This item should be a seq input. | ||||
3060 | if (!PySequence_Check(item)) { | ||||
3061 | VLOG(1)(__builtin_expect(!!(!(([](int level, const char* fname) { static const bool vmodule_activated = ::tensorflow::internal::LogMessage ::VmoduleActivated(fname, level); return vmodule_activated; } )(1, "tensorflow/python/eager/pywrap_tfe_src.cc"))), 1)) ? (void )0 : ::tensorflow::internal::Voidifier() & ::tensorflow:: internal::LogMessage("tensorflow/python/eager/pywrap_tfe_src.cc" , 3061, tensorflow::INFO) << "Falling back to slow path for Op \"" << op_def.name() | ||||
3062 | << "\", Input \"" << op_def.input_arg(i).name() | ||||
3063 | << "\" since we expected a sequence, but got " | ||||
3064 | << item->ob_type->tp_name; | ||||
3065 | return false; | ||||
3066 | } | ||||
3067 | tensorflow::Safe_PyObjectPtr fast_item( | ||||
3068 | PySequence_Fast(item, "Could not parse sequence.")); | ||||
3069 | if (fast_item.get() == nullptr) { | ||||
3070 | return false; | ||||
3071 | } | ||||
3072 | int len = PySequence_Fast_GET_SIZE(fast_item.get())(((((((PyObject*)(fast_item.get()))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((static_cast<void> (0)), (((PyVarObject*)(fast_item.get()))->ob_size)) : ((( PyVarObject*)(((static_cast<void> (0)), (PyTupleObject * )(fast_item.get()))))->ob_size)); | ||||
3073 | PyObject** fast_item_array = PySequence_Fast_ITEMS(fast_item.get())(((((((PyObject*)(fast_item.get()))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((PyListObject *)(fast_item .get()))->ob_item : ((PyTupleObject *)(fast_item.get()))-> ob_item); | ||||
3074 | for (Py_ssize_t j = 0; j < len; j++) { | ||||
3075 | PyObject* inner_item = fast_item_array[j]; | ||||
3076 | if (!CheckOneInput(inner_item)) { | ||||
3077 | VLOG(1)(__builtin_expect(!!(!(([](int level, const char* fname) { static const bool vmodule_activated = ::tensorflow::internal::LogMessage ::VmoduleActivated(fname, level); return vmodule_activated; } )(1, "tensorflow/python/eager/pywrap_tfe_src.cc"))), 1)) ? (void )0 : ::tensorflow::internal::Voidifier() & ::tensorflow:: internal::LogMessage("tensorflow/python/eager/pywrap_tfe_src.cc" , 3077, tensorflow::INFO) << "Falling back to slow path for Op \"" << op_def.name() | ||||
3078 | << "\", Input \"" << op_def.input_arg(i).name() | ||||
3079 | << "\", Index " << j | ||||
3080 | << " since we expected an EagerTensor/ResourceVariable, " | ||||
3081 | "but got " | ||||
3082 | << inner_item->ob_type->tp_name; | ||||
3083 | return false; | ||||
3084 | } | ||||
3085 | } | ||||
3086 | } else if (!CheckOneInput(item)) { | ||||
3087 | VLOG(1)(__builtin_expect(!!(!(([](int level, const char* fname) { static const bool vmodule_activated = ::tensorflow::internal::LogMessage ::VmoduleActivated(fname, level); return vmodule_activated; } )(1, "tensorflow/python/eager/pywrap_tfe_src.cc"))), 1)) ? (void )0 : ::tensorflow::internal::Voidifier() & ::tensorflow:: internal::LogMessage("tensorflow/python/eager/pywrap_tfe_src.cc" , 3087, tensorflow::INFO) | ||||
3088 | << "Falling back to slow path for Op \"" << op_def.name() | ||||
3089 | << "\", Input \"" << op_def.input_arg(i).name() | ||||
3090 | << "\" since we expected an EagerTensor/ResourceVariable, but got " | ||||
3091 | << item->ob_type->tp_name; | ||||
3092 | return false; | ||||
3093 | } | ||||
3094 | } | ||||
3095 | |||||
3096 | return true; | ||||
3097 | } | ||||
3098 | |||||
3099 | tensorflow::DataType MaybeGetDType(PyObject* item) { | ||||
3100 | if (EagerTensor_CheckExact(item) || CheckResourceVariable(item)) { | ||||
3101 | return tensorflow::PyTensor_DataType(item); | ||||
3102 | } | ||||
3103 | |||||
3104 | return tensorflow::DT_INVALID; | ||||
3105 | } | ||||
3106 | |||||
3107 | tensorflow::DataType MaybeGetDTypeForAttr(const string& attr, | ||||
3108 | FastPathOpExecInfo* op_exec_info) { | ||||
3109 | auto cached_it = op_exec_info->cached_dtypes.find(attr); | ||||
3110 | if (cached_it != op_exec_info->cached_dtypes.end()) { | ||||
3111 | return cached_it->second; | ||||
3112 | } | ||||
3113 | |||||
3114 | auto it = op_exec_info->attr_to_inputs_map->find(attr); | ||||
3115 | if (it == op_exec_info->attr_to_inputs_map->end()) { | ||||
3116 | // No other inputs - this should never happen. | ||||
3117 | return tensorflow::DT_INVALID; | ||||
3118 | } | ||||
3119 | |||||
3120 | for (const auto& input_info : it->second) { | ||||
3121 | PyObject* item = PyTuple_GET_ITEM((((static_cast<void> (0)), (PyTupleObject *)(op_exec_info ->args))->ob_item[FAST_PATH_EXECUTE_ARG_INPUT_START + input_info .i]) | ||||
3122 | op_exec_info->args, FAST_PATH_EXECUTE_ARG_INPUT_START + input_info.i)(((static_cast<void> (0)), (PyTupleObject *)(op_exec_info ->args))->ob_item[FAST_PATH_EXECUTE_ARG_INPUT_START + input_info .i]); | ||||
3123 | if (input_info.is_list) { | ||||
3124 | tensorflow::Safe_PyObjectPtr fast_item( | ||||
3125 | PySequence_Fast(item, "Unable to allocate")); | ||||
3126 | int len = PySequence_Fast_GET_SIZE(fast_item.get())(((((((PyObject*)(fast_item.get()))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((static_cast<void> (0)), (((PyVarObject*)(fast_item.get()))->ob_size)) : ((( PyVarObject*)(((static_cast<void> (0)), (PyTupleObject * )(fast_item.get()))))->ob_size)); | ||||
3127 | PyObject** fast_item_array = PySequence_Fast_ITEMS(fast_item.get())(((((((PyObject*)(fast_item.get()))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((PyListObject *)(fast_item .get()))->ob_item : ((PyTupleObject *)(fast_item.get()))-> ob_item); | ||||
3128 | for (int i = 0; i < len; i++) { | ||||
3129 | auto dtype = MaybeGetDType(fast_item_array[i]); | ||||
3130 | if (dtype != tensorflow::DT_INVALID) return dtype; | ||||
3131 | } | ||||
3132 | } else { | ||||
3133 | auto dtype = MaybeGetDType(item); | ||||
3134 | if (dtype != tensorflow::DT_INVALID) return dtype; | ||||
3135 | } | ||||
3136 | } | ||||
3137 | |||||
3138 | auto default_it = op_exec_info->default_dtypes->find(attr); | ||||
3139 | if (default_it != op_exec_info->default_dtypes->end()) { | ||||
3140 | return default_it->second; | ||||
3141 | } | ||||
3142 | |||||
3143 | return tensorflow::DT_INVALID; | ||||
3144 | } | ||||
3145 | |||||
3146 | PyObject* CopySequenceSettingIndicesToNull( | ||||
3147 | PyObject* seq, const tensorflow::gtl::FlatSet<int>& indices) { | ||||
3148 | tensorflow::Safe_PyObjectPtr fast_seq( | ||||
3149 | PySequence_Fast(seq, "unable to allocate")); | ||||
3150 | int len = PySequence_Fast_GET_SIZE(fast_seq.get())(((((((PyObject*)(fast_seq.get()))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((static_cast<void> (0)), (((PyVarObject*)(fast_seq.get()))->ob_size)) : (((PyVarObject *)(((static_cast<void> (0)), (PyTupleObject *)(fast_seq .get()))))->ob_size)); | ||||
3151 | PyObject** fast_seq_array = PySequence_Fast_ITEMS(fast_seq.get())(((((((PyObject*)(fast_seq.get()))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((PyListObject *)(fast_seq .get()))->ob_item : ((PyTupleObject *)(fast_seq.get()))-> ob_item); | ||||
3152 | PyObject* result = PyTuple_New(len); | ||||
3153 | for (int i = 0; i < len; i++) { | ||||
3154 | PyObject* item; | ||||
3155 | if (indices.find(i) != indices.end()) { | ||||
3156 | item = Py_None(&_Py_NoneStruct); | ||||
3157 | } else { | ||||
3158 | item = fast_seq_array[i]; | ||||
3159 | } | ||||
3160 | Py_INCREF(item)_Py_INCREF(((PyObject*)(item))); | ||||
3161 | PyTuple_SET_ITEM(result, i, item)PyTuple_SetItem(result, i, item); | ||||
3162 | } | ||||
3163 | return result; | ||||
3164 | } | ||||
3165 | |||||
3166 | PyObject* RecordGradient(PyObject* op_name, PyObject* inputs, PyObject* attrs, | ||||
3167 | PyObject* results, | ||||
3168 | PyObject* forward_pass_name_scope = nullptr) { | ||||
3169 | std::vector<int64_t> input_ids = MakeTensorIDList(inputs); | ||||
3170 | if (PyErr_Occurred()) return nullptr; | ||||
3171 | std::vector<tensorflow::DataType> input_dtypes = MakeTensorDtypeList(inputs); | ||||
3172 | if (PyErr_Occurred()) return nullptr; | ||||
3173 | |||||
3174 | bool should_record = false; | ||||
3175 | for (TFE_Py_Tape* tape : SafeTapeSet()) { | ||||
3176 | if (tape->tape->ShouldRecord(input_ids, input_dtypes)) { | ||||
3177 | should_record = true; | ||||
3178 | break; | ||||
3179 | } | ||||
3180 | } | ||||
3181 | if (!should_record) { | ||||
3182 | for (TFE_Py_ForwardAccumulator* accumulator : SafeAccumulatorSet()) { | ||||
3183 | if (accumulator->accumulator->ShouldRecord(input_ids, input_dtypes)) { | ||||
3184 | should_record = true; | ||||
3185 | break; | ||||
3186 | } | ||||
3187 | } | ||||
3188 | } | ||||
3189 | if (!should_record) Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (& _Py_NoneStruct); | ||||
3190 | |||||
3191 | string c_op_name = TFE_GetPythonString(op_name); | ||||
3192 | |||||
3193 | PyObject* op_outputs; | ||||
3194 | bool op_outputs_tuple_created = false; | ||||
3195 | |||||
3196 | if (const auto unused_output_indices = | ||||
3197 | OpGradientUnusedOutputIndices(c_op_name)) { | ||||
3198 | if (unused_output_indices->empty()) { | ||||
3199 | op_outputs = Py_None(&_Py_NoneStruct); | ||||
3200 | } else { | ||||
3201 | op_outputs_tuple_created = true; | ||||
3202 | op_outputs = | ||||
3203 | CopySequenceSettingIndicesToNull(results, *unused_output_indices); | ||||
3204 | } | ||||
3205 | } else { | ||||
3206 | op_outputs = results; | ||||
3207 | } | ||||
3208 | |||||
3209 | PyObject* op_inputs; | ||||
3210 | bool op_inputs_tuple_created = false; | ||||
3211 | |||||
3212 | if (const auto unused_input_indices = | ||||
3213 | OpGradientUnusedInputIndices(c_op_name)) { | ||||
3214 | if (unused_input_indices->empty()) { | ||||
3215 | op_inputs = Py_None(&_Py_NoneStruct); | ||||
3216 | } else { | ||||
3217 | op_inputs_tuple_created = true; | ||||
3218 | op_inputs = | ||||
3219 | CopySequenceSettingIndicesToNull(inputs, *unused_input_indices); | ||||
3220 | } | ||||
3221 | } else { | ||||
3222 | op_inputs = inputs; | ||||
3223 | } | ||||
3224 | |||||
3225 | tensorflow::eager::ForwardFunction<PyObject> py_forward_function( | ||||
3226 | [op_name, attrs, inputs, results]( | ||||
3227 | const std::vector<PyObject*>& input_tangents, | ||||
3228 | std::vector<PyObject*>* output_tangents, bool use_batch) { | ||||
3229 | return CallJVPFunction(op_name, attrs, inputs, results, input_tangents, | ||||
3230 | output_tangents, use_batch); | ||||
3231 | }); | ||||
3232 | tensorflow::eager::ForwardFunction<PyObject>* forward_function; | ||||
3233 | if (c_op_name == "While" || c_op_name == "StatelessWhile" || | ||||
3234 | c_op_name == "If" || c_op_name == "StatelessIf") { | ||||
3235 | // Control flow contains non-hashable attributes. Handling them in Python is | ||||
3236 | // a headache, so instead we'll stay as close to GradientTape's handling as | ||||
3237 | // possible (a null forward function means the accumulator forwards to a | ||||
3238 | // tape). | ||||
3239 | // | ||||
3240 | // This is safe to do since we'll only see control flow when graph building, | ||||
3241 | // in which case we can rely on pruning. | ||||
3242 | forward_function = nullptr; | ||||
3243 | } else { | ||||
3244 | forward_function = &py_forward_function; | ||||
3245 | } | ||||
3246 | |||||
3247 | PyObject* num_inputs = PyLong_FromLong(PySequence_Size(inputs)); | ||||
3248 | |||||
3249 | if (!forward_pass_name_scope) forward_pass_name_scope = Py_None(&_Py_NoneStruct); | ||||
3250 | |||||
3251 | TapeSetRecordOperation( | ||||
3252 | op_name, inputs, results, input_ids, input_dtypes, | ||||
3253 | [op_name, attrs, num_inputs, op_inputs, op_outputs, | ||||
3254 | forward_pass_name_scope]() { | ||||
3255 | Py_INCREF(op_name)_Py_INCREF(((PyObject*)(op_name))); | ||||
3256 | Py_INCREF(attrs)_Py_INCREF(((PyObject*)(attrs))); | ||||
3257 | Py_INCREF(num_inputs)_Py_INCREF(((PyObject*)(num_inputs))); | ||||
3258 | Py_INCREF(op_inputs)_Py_INCREF(((PyObject*)(op_inputs))); | ||||
3259 | Py_INCREF(op_outputs)_Py_INCREF(((PyObject*)(op_outputs))); | ||||
3260 | Py_INCREF(forward_pass_name_scope)_Py_INCREF(((PyObject*)(forward_pass_name_scope))); | ||||
3261 | PyBackwardFunction* function = new PyBackwardFunction( | ||||
3262 | [op_name, attrs, num_inputs, op_inputs, op_outputs, | ||||
3263 | forward_pass_name_scope]( | ||||
3264 | PyObject* output_grads, | ||||
3265 | const std::vector<int64_t>& unneeded_gradients) { | ||||
3266 | if (PyErr_Occurred()) { | ||||
3267 | return static_cast<PyObject*>(nullptr); | ||||
3268 | } | ||||
3269 | tensorflow::Safe_PyObjectPtr skip_input_indices; | ||||
3270 | if (!unneeded_gradients.empty()) { | ||||
3271 | skip_input_indices.reset( | ||||
3272 | PyTuple_New(unneeded_gradients.size())); | ||||
3273 | for (int i = 0; i < unneeded_gradients.size(); i++) { | ||||
3274 | PyTuple_SET_ITEM(PyTuple_SetItem(skip_input_indices.get(), i, GetPythonObjectFromInt (unneeded_gradients[i])) | ||||
3275 | skip_input_indices.get(), i,PyTuple_SetItem(skip_input_indices.get(), i, GetPythonObjectFromInt (unneeded_gradients[i])) | ||||
3276 | GetPythonObjectFromInt(unneeded_gradients[i]))PyTuple_SetItem(skip_input_indices.get(), i, GetPythonObjectFromInt (unneeded_gradients[i])); | ||||
3277 | } | ||||
3278 | } else { | ||||
3279 | Py_INCREF(Py_None)_Py_INCREF(((PyObject*)((&_Py_NoneStruct)))); | ||||
3280 | skip_input_indices.reset(Py_None(&_Py_NoneStruct)); | ||||
3281 | } | ||||
3282 | tensorflow::Safe_PyObjectPtr callback_args(Py_BuildValue( | ||||
3283 | "OOOOOOOO", op_name, attrs, num_inputs, op_inputs, op_outputs, | ||||
3284 | output_grads, skip_input_indices.get(), | ||||
3285 | forward_pass_name_scope)); | ||||
3286 | |||||
3287 | tensorflow::Safe_PyObjectPtr result( | ||||
3288 | PyObject_CallObject(gradient_function, callback_args.get())); | ||||
3289 | |||||
3290 | if (PyErr_Occurred()) return static_cast<PyObject*>(nullptr); | ||||
3291 | |||||
3292 | return tensorflow::swig::Flatten(result.get()); | ||||
3293 | }); | ||||
3294 | return function; | ||||
3295 | }, | ||||
3296 | [op_name, attrs, num_inputs, op_inputs, op_outputs, | ||||
3297 | forward_pass_name_scope](PyBackwardFunction* backward_function) { | ||||
3298 | Py_DECREF(op_name)_Py_DECREF(((PyObject*)(op_name))); | ||||
3299 | Py_DECREF(attrs)_Py_DECREF(((PyObject*)(attrs))); | ||||
3300 | Py_DECREF(num_inputs)_Py_DECREF(((PyObject*)(num_inputs))); | ||||
3301 | Py_DECREF(op_inputs)_Py_DECREF(((PyObject*)(op_inputs))); | ||||
3302 | Py_DECREF(op_outputs)_Py_DECREF(((PyObject*)(op_outputs))); | ||||
3303 | Py_DECREF(forward_pass_name_scope)_Py_DECREF(((PyObject*)(forward_pass_name_scope))); | ||||
3304 | |||||
3305 | delete backward_function; | ||||
3306 | }, | ||||
3307 | forward_function); | ||||
3308 | |||||
3309 | Py_DECREF(num_inputs)_Py_DECREF(((PyObject*)(num_inputs))); | ||||
3310 | if (op_outputs_tuple_created) Py_DECREF(op_outputs)_Py_DECREF(((PyObject*)(op_outputs))); | ||||
3311 | if (op_inputs_tuple_created) Py_DECREF(op_inputs)_Py_DECREF(((PyObject*)(op_inputs))); | ||||
3312 | |||||
3313 | if (PyErr_Occurred()) { | ||||
3314 | return nullptr; | ||||
3315 | } | ||||
3316 | |||||
3317 | Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (& _Py_NoneStruct); | ||||
3318 | } | ||||
3319 | |||||
3320 | void MaybeNotifyVariableAccessed(PyObject* input) { | ||||
3321 | DCHECK(CheckResourceVariable(input))while (false && (CheckResourceVariable(input))) ::tensorflow ::internal::LogMessageFatal("tensorflow/python/eager/pywrap_tfe_src.cc" , 3321); | ||||
3322 | DCHECK(PyObject_HasAttrString(input, "_trainable"))while (false && (PyObject_HasAttrString(input, "_trainable" ))) ::tensorflow::internal::LogMessageFatal("tensorflow/python/eager/pywrap_tfe_src.cc" , 3322); | ||||
3323 | |||||
3324 | tensorflow::Safe_PyObjectPtr trainable( | ||||
3325 | PyObject_GetAttrString(input, "_trainable")); | ||||
3326 | if (trainable.get() == Py_False((PyObject *) &_Py_FalseStruct)) return; | ||||
3327 | TFE_Py_TapeVariableAccessed(input); | ||||
3328 | TFE_Py_VariableWatcherVariableAccessed(input); | ||||
3329 | } | ||||
3330 | |||||
3331 | bool ReadVariableOp(const FastPathOpExecInfo& parent_op_exec_info, | ||||
3332 | PyObject* input, tensorflow::Safe_PyObjectPtr* output, | ||||
3333 | TF_Status* status) { | ||||
3334 | MaybeNotifyVariableAccessed(input); | ||||
3335 | |||||
3336 | TFE_Op* op = TFE_NewOp(parent_op_exec_info.ctx, "ReadVariableOp", status); | ||||
3337 | auto cleaner = tensorflow::gtl::MakeCleanup([op] { TFE_DeleteOp(op); }); | ||||
3338 | if (MaybeRaiseExceptionFromTFStatus(status, nullptr)) return false; | ||||
3339 | |||||
3340 | TFE_OpSetDevice(op, parent_op_exec_info.device_name, status); | ||||
3341 | if (MaybeRaiseExceptionFromTFStatus(status, nullptr)) return false; | ||||
3342 | |||||
3343 | // Set dtype | ||||
3344 | DCHECK(PyObject_HasAttrString(input, "_dtype"))while (false && (PyObject_HasAttrString(input, "_dtype" ))) ::tensorflow::internal::LogMessageFatal("tensorflow/python/eager/pywrap_tfe_src.cc" , 3344); | ||||
3345 | tensorflow::Safe_PyObjectPtr dtype(PyObject_GetAttrString(input, "_dtype")); | ||||
3346 | int value; | ||||
3347 | if (!ParseTypeValue("_dtype", dtype.get(), status, &value)) { | ||||
3348 | return false; | ||||
3349 | } | ||||
3350 | TFE_OpSetAttrType(op, "dtype", static_cast<TF_DataType>(value)); | ||||
3351 | |||||
3352 | // Get handle | ||||
3353 | tensorflow::Safe_PyObjectPtr handle(PyObject_GetAttrString(input, "_handle")); | ||||
3354 | if (!EagerTensor_CheckExact(handle.get())) return false; | ||||
3355 | TFE_OpAddInput(op, EagerTensor_Handle(handle.get()), status); | ||||
3356 | if (MaybeRaiseExceptionFromTFStatus(status, nullptr)) return false; | ||||
3357 | |||||
3358 | int num_retvals = 1; | ||||
3359 | TFE_TensorHandle* output_handle; | ||||
3360 | TFE_Execute(op, &output_handle, &num_retvals, status); | ||||
3361 | if (MaybeRaiseExceptionFromTFStatus(status, nullptr)) return false; | ||||
3362 | |||||
3363 | // Always create the py object (and correctly DECREF it) from the returned | ||||
3364 | // value, else the data will leak. | ||||
3365 | output->reset(EagerTensorFromHandle(output_handle)); | ||||
3366 | |||||
3367 | // TODO(nareshmodi): Should we run post exec callbacks here? | ||||
3368 | if (parent_op_exec_info.run_gradient_callback) { | ||||
3369 | tensorflow::Safe_PyObjectPtr inputs(PyTuple_New(1)); | ||||
3370 | PyTuple_SET_ITEM(inputs.get(), 0, handle.release())PyTuple_SetItem(inputs.get(), 0, handle.release()); | ||||
3371 | |||||
3372 | tensorflow::Safe_PyObjectPtr outputs(PyTuple_New(1)); | ||||
3373 | Py_INCREF(output->get())_Py_INCREF(((PyObject*)(output->get()))); // stay alive after since tuple steals. | ||||
3374 | PyTuple_SET_ITEM(outputs.get(), 0, output->get())PyTuple_SetItem(outputs.get(), 0, output->get()); | ||||
3375 | |||||
3376 | tensorflow::Safe_PyObjectPtr op_string( | ||||
3377 | GetPythonObjectFromString("ReadVariableOp")); | ||||
3378 | if (!RecordGradient(op_string.get(), inputs.get(), Py_None(&_Py_NoneStruct), | ||||
3379 | outputs.get())) { | ||||
3380 | return false; | ||||
3381 | } | ||||
3382 | } | ||||
3383 | |||||
3384 | return true; | ||||
3385 | } | ||||
3386 | |||||
3387 | // Supports 3 cases at the moment: | ||||
3388 | // i) input is an EagerTensor. | ||||
3389 | // ii) input is a ResourceVariable - in this case, the is_variable param is | ||||
3390 | // set to true. | ||||
3391 | // iii) input is an arbitrary python list/tuple (note, this handling doesn't | ||||
3392 | // support packing). | ||||
3393 | // | ||||
3394 | // NOTE: dtype_hint_getter must *always* return a PyObject that can be | ||||
3395 | // decref'd. So if no hint is found, Py_RETURN_NONE (which correctly | ||||
3396 | // increfs Py_None). | ||||
3397 | // | ||||
3398 | // NOTE: This function sets a python error directly, and returns false. | ||||
3399 | // TF_Status is only passed since we don't want to have to reallocate it. | ||||
3400 | bool ConvertToTensor( | ||||
3401 | const FastPathOpExecInfo& op_exec_info, PyObject* input, | ||||
3402 | tensorflow::Safe_PyObjectPtr* output_handle, | ||||
3403 | // This gets a hint for this particular input. | ||||
3404 | const std::function<tensorflow::DataType()>& dtype_hint_getter, | ||||
3405 | // This sets the dtype after conversion is complete. | ||||
3406 | const std::function<void(const tensorflow::DataType dtype)>& dtype_setter, | ||||
3407 | TF_Status* status) { | ||||
3408 | if (EagerTensor_CheckExact(input)) { | ||||
3409 | Py_INCREF(input)_Py_INCREF(((PyObject*)(input))); | ||||
3410 | output_handle->reset(input); | ||||
3411 | return true; | ||||
3412 | } else if (CheckResourceVariable(input)) { | ||||
3413 | return ReadVariableOp(op_exec_info, input, output_handle, status); | ||||
3414 | } | ||||
3415 | |||||
3416 | // The hint comes from a supposedly similarly typed tensor. | ||||
3417 | tensorflow::DataType dtype_hint = dtype_hint_getter(); | ||||
3418 | |||||
3419 | TFE_TensorHandle* handle = tensorflow::ConvertToEagerTensor( | ||||
3420 | op_exec_info.ctx, input, dtype_hint, op_exec_info.device_name); | ||||
3421 | if (handle == nullptr) { | ||||
3422 | return MaybeRaiseExceptionFromTFStatus(status, nullptr); | ||||
3423 | } | ||||
3424 | |||||
3425 | output_handle->reset(EagerTensorFromHandle(handle)); | ||||
3426 | dtype_setter( | ||||
3427 | static_cast<tensorflow::DataType>(TFE_TensorHandleDataType(handle))); | ||||
3428 | |||||
3429 | return true; | ||||
3430 | } | ||||
3431 | |||||
3432 | // Adds input and type attr to the op, and to the list of flattened | ||||
3433 | // inputs/attrs. | ||||
3434 | bool AddInputToOp(FastPathOpExecInfo* op_exec_info, PyObject* input, | ||||
3435 | const bool add_type_attr, | ||||
3436 | const tensorflow::OpDef::ArgDef& input_arg, | ||||
3437 | std::vector<tensorflow::Safe_PyObjectPtr>* flattened_attrs, | ||||
3438 | std::vector<tensorflow::Safe_PyObjectPtr>* flattened_inputs, | ||||
3439 | TFE_Op* op, TF_Status* status) { | ||||
3440 | // py_eager_tensor's ownership is transferred to flattened_inputs if it is | ||||
3441 | // required, else the object is destroyed and DECREF'd when the object goes | ||||
3442 | // out of scope in this function. | ||||
3443 | tensorflow::Safe_PyObjectPtr py_eager_tensor = nullptr; | ||||
3444 | |||||
3445 | if (!ConvertToTensor( | ||||
3446 | *op_exec_info, input, &py_eager_tensor, | ||||
3447 | [&]() { | ||||
3448 | if (input_arg.type() != tensorflow::DataType::DT_INVALID) { | ||||
3449 | return input_arg.type(); | ||||
3450 | } | ||||
3451 | return MaybeGetDTypeForAttr(input_arg.type_attr(), op_exec_info); | ||||
3452 | }, | ||||
3453 | [&](const tensorflow::DataType dtype) { | ||||
3454 | op_exec_info->cached_dtypes[input_arg.type_attr()] = dtype; | ||||
3455 | }, | ||||
3456 | status)) { | ||||
3457 | return false; | ||||
3458 | } | ||||
3459 | |||||
3460 | TFE_TensorHandle* input_handle = EagerTensor_Handle(py_eager_tensor.get()); | ||||
3461 | |||||
3462 | if (add_type_attr && !input_arg.type_attr().empty()) { | ||||
3463 | auto dtype = TFE_TensorHandleDataType(input_handle); | ||||
3464 | TFE_OpSetAttrType(op, input_arg.type_attr().data(), dtype); | ||||
3465 | if (flattened_attrs != nullptr) { | ||||
3466 | flattened_attrs->emplace_back( | ||||
3467 | GetPythonObjectFromString(input_arg.type_attr())); | ||||
3468 | flattened_attrs->emplace_back(PyLong_FromLong(dtype)); | ||||
3469 | } | ||||
3470 | } | ||||
3471 | |||||
3472 | if (flattened_inputs != nullptr) { | ||||
3473 | flattened_inputs->emplace_back(std::move(py_eager_tensor)); | ||||
3474 | } | ||||
3475 | |||||
3476 | TFE_OpAddInput(op, input_handle, status); | ||||
3477 | if (MaybeRaiseExceptionFromTFStatus(status, nullptr)) { | ||||
3478 | return false; | ||||
3479 | } | ||||
3480 | |||||
3481 | return true; | ||||
3482 | } | ||||
3483 | |||||
3484 | const char* GetDeviceName(PyObject* py_device_name) { | ||||
3485 | if (py_device_name != Py_None(&_Py_NoneStruct)) { | ||||
3486 | return TFE_GetPythonString(py_device_name); | ||||
3487 | } | ||||
3488 | return nullptr; | ||||
3489 | } | ||||
3490 | |||||
3491 | bool RaiseIfNotPySequence(PyObject* seq, const string& attr_name) { | ||||
3492 | if (!PySequence_Check(seq)) { | ||||
3493 | PyErr_SetString(PyExc_TypeError, | ||||
3494 | Printf("expected a sequence for attr %s, got %s instead", | ||||
3495 | attr_name.data(), seq->ob_type->tp_name) | ||||
3496 | .data()); | ||||
3497 | |||||
3498 | return false; | ||||
3499 | } | ||||
3500 | if (PyArray_Check(seq)((((PyObject*)(seq))->ob_type) == (&(*(PyTypeObject *) _tensorflow_numpy_api[2])) || PyType_IsSubtype((((PyObject*)( seq))->ob_type), (&(*(PyTypeObject *)_tensorflow_numpy_api [2])))) && | ||||
3501 | PyArray_NDIM(reinterpret_cast<PyArrayObject*>(seq)) != 1) { | ||||
3502 | PyErr_SetString(PyExc_ValueError, | ||||
3503 | Printf("expected a sequence for attr %s, got an ndarray " | ||||
3504 | "with rank %d instead", | ||||
3505 | attr_name.data(), | ||||
3506 | PyArray_NDIM(reinterpret_cast<PyArrayObject*>(seq))) | ||||
3507 | .data()); | ||||
3508 | return false; | ||||
3509 | } | ||||
3510 | return true; | ||||
3511 | } | ||||
3512 | |||||
3513 | bool RunCallbacks( | ||||
3514 | const FastPathOpExecInfo& op_exec_info, PyObject* args, | ||||
3515 | int num_inferred_attrs, | ||||
3516 | const std::vector<tensorflow::Safe_PyObjectPtr>& flattened_inputs, | ||||
3517 | const std::vector<tensorflow::Safe_PyObjectPtr>& flattened_attrs, | ||||
3518 | PyObject* flattened_result) { | ||||
3519 | DCHECK(op_exec_info.run_callbacks)while (false && (op_exec_info.run_callbacks)) ::tensorflow ::internal::LogMessageFatal("tensorflow/python/eager/pywrap_tfe_src.cc" , 3519); | ||||
3520 | |||||
3521 | tensorflow::Safe_PyObjectPtr inputs(PyTuple_New(flattened_inputs.size())); | ||||
3522 | for (int i = 0; i < flattened_inputs.size(); i++) { | ||||
3523 | PyObject* input = flattened_inputs[i].get(); | ||||
3524 | Py_INCREF(input)_Py_INCREF(((PyObject*)(input))); | ||||
3525 | PyTuple_SET_ITEM(inputs.get(), i, input)PyTuple_SetItem(inputs.get(), i, input); | ||||
3526 | } | ||||
3527 | |||||
3528 | int num_non_inferred_attrs = PyTuple_GET_SIZE(args)(((PyVarObject*)(((static_cast<void> (0)), (PyTupleObject *)(args))))->ob_size) - num_inferred_attrs; | ||||
3529 | int num_attrs = flattened_attrs.size() + num_non_inferred_attrs; | ||||
3530 | tensorflow::Safe_PyObjectPtr attrs(PyTuple_New(num_attrs)); | ||||
3531 | |||||
3532 | for (int i = 0; i < num_non_inferred_attrs; i++) { | ||||
3533 | auto* attr = PyTuple_GET_ITEM(args, num_inferred_attrs + i)(((static_cast<void> (0)), (PyTupleObject *)(args))-> ob_item[num_inferred_attrs + i]); | ||||
3534 | Py_INCREF(attr)_Py_INCREF(((PyObject*)(attr))); | ||||
3535 | PyTuple_SET_ITEM(attrs.get(), i, attr)PyTuple_SetItem(attrs.get(), i, attr); | ||||
3536 | } | ||||
3537 | |||||
3538 | for (int i = num_non_inferred_attrs; i < num_attrs; i++) { | ||||
3539 | PyObject* attr_or_name = | ||||
3540 | flattened_attrs.at(i - num_non_inferred_attrs).get(); | ||||
3541 | Py_INCREF(attr_or_name)_Py_INCREF(((PyObject*)(attr_or_name))); | ||||
3542 | PyTuple_SET_ITEM(attrs.get(), i, attr_or_name)PyTuple_SetItem(attrs.get(), i, attr_or_name); | ||||
3543 | } | ||||
3544 | |||||
3545 | if (op_exec_info.run_gradient_callback) { | ||||
3546 | if (!RecordGradient(op_exec_info.op_name, inputs.get(), attrs.get(), | ||||
3547 | flattened_result)) { | ||||
3548 | return false; | ||||
3549 | } | ||||
3550 | } | ||||
3551 | |||||
3552 | if (op_exec_info.run_post_exec_callbacks) { | ||||
3553 | tensorflow::Safe_PyObjectPtr callback_args( | ||||
3554 | Py_BuildValue("OOOOO", op_exec_info.op_name, inputs.get(), attrs.get(), | ||||
3555 | flattened_result, op_exec_info.name)); | ||||
3556 | for (Py_ssize_t i = 0; i < PyList_Size(op_exec_info.callbacks); i++) { | ||||
3557 | PyObject* callback_fn = PyList_GET_ITEM(op_exec_info.callbacks, i)(((PyListObject *)(op_exec_info.callbacks))->ob_item[i]); | ||||
3558 | if (!PyCallable_Check(callback_fn)) { | ||||
3559 | PyErr_SetString( | ||||
3560 | PyExc_TypeError, | ||||
3561 | Printf("expected a function for " | ||||
3562 | "post execution callback in index %ld, got %s instead", | ||||
3563 | i, callback_fn->ob_type->tp_name) | ||||
3564 | .c_str()); | ||||
3565 | return false; | ||||
3566 | } | ||||
3567 | PyObject* callback_result = | ||||
3568 | PyObject_CallObject(callback_fn, callback_args.get()); | ||||
3569 | if (!callback_result) { | ||||
3570 | return false; | ||||
3571 | } | ||||
3572 | Py_DECREF(callback_result)_Py_DECREF(((PyObject*)(callback_result))); | ||||
3573 | } | ||||
3574 | } | ||||
3575 | |||||
3576 | return true; | ||||
3577 | } | ||||
3578 | |||||
3579 | } // namespace | ||||
3580 | |||||
3581 | PyObject* TFE_Py_FastPathExecute_C(PyObject* args) { | ||||
3582 | tensorflow::profiler::TraceMe activity( | ||||
3583 | "TFE_Py_FastPathExecute_C", tensorflow::profiler::TraceMeLevel::kInfo); | ||||
3584 | Py_ssize_t args_size = PyTuple_GET_SIZE(args)(((PyVarObject*)(((static_cast<void> (0)), (PyTupleObject *)(args))))->ob_size); | ||||
3585 | if (args_size < FAST_PATH_EXECUTE_ARG_INPUT_START) { | ||||
3586 | PyErr_SetString( | ||||
3587 | PyExc_ValueError, | ||||
3588 | Printf("There must be at least %d items in the input tuple.", | ||||
3589 | FAST_PATH_EXECUTE_ARG_INPUT_START) | ||||
3590 | .c_str()); | ||||
3591 | return nullptr; | ||||
3592 | } | ||||
3593 | |||||
3594 | FastPathOpExecInfo op_exec_info; | ||||
3595 | |||||
3596 | PyObject* py_eager_context = | ||||
3597 | PyTuple_GET_ITEM(args, FAST_PATH_EXECUTE_ARG_CONTEXT)(((static_cast<void> (0)), (PyTupleObject *)(args))-> ob_item[FAST_PATH_EXECUTE_ARG_CONTEXT]); | ||||
3598 | |||||
3599 | // TODO(edoper): Use interned string here | ||||
3600 | PyObject* eager_context_handle = | ||||
3601 | PyObject_GetAttrString(py_eager_context, "_context_handle"); | ||||
3602 | |||||
3603 | TFE_Context* ctx = reinterpret_cast<TFE_Context*>( | ||||
3604 | PyCapsule_GetPointer(eager_context_handle, nullptr)); | ||||
3605 | op_exec_info.ctx = ctx; | ||||
3606 | op_exec_info.args = args; | ||||
3607 | |||||
3608 | if (ctx == nullptr) { | ||||
3609 | // The context hasn't been initialized. It will be in the slow path. | ||||
3610 | RaiseFallbackException( | ||||
3611 | "This function does not handle the case of the path where " | ||||
3612 | "all inputs are not already EagerTensors."); | ||||
3613 | return nullptr; | ||||
3614 | } | ||||
3615 | |||||
3616 | auto* tld = tensorflow::GetEagerContextThreadLocalData(py_eager_context); | ||||
3617 | if (tld == nullptr) { | ||||
3618 | return nullptr; | ||||
3619 | } | ||||
3620 | op_exec_info.device_name = GetDeviceName(tld->device_name.get()); | ||||
3621 | op_exec_info.callbacks = tld->op_callbacks.get(); | ||||
3622 | |||||
3623 | op_exec_info.op_name = PyTuple_GET_ITEM(args, FAST_PATH_EXECUTE_ARG_OP_NAME)(((static_cast<void> (0)), (PyTupleObject *)(args))-> ob_item[FAST_PATH_EXECUTE_ARG_OP_NAME]); | ||||
3624 | op_exec_info.name = PyTuple_GET_ITEM(args, FAST_PATH_EXECUTE_ARG_NAME)(((static_cast<void> (0)), (PyTupleObject *)(args))-> ob_item[FAST_PATH_EXECUTE_ARG_NAME]); | ||||
3625 | |||||
3626 | // TODO(nareshmodi): Add a benchmark for the fast-path with gradient callbacks | ||||
3627 | // (similar to benchmark_tf_gradient_function_*). Also consider using an | ||||
3628 | // InlinedVector for flattened_attrs and flattened_inputs if the benchmarks | ||||
3629 | // point out problems with heap allocs. | ||||
3630 | op_exec_info.run_gradient_callback = | ||||
3631 | !*ThreadTapeIsStopped() && HasAccumulatorOrTape(); | ||||
3632 | op_exec_info.run_post_exec_callbacks = | ||||
3633 | op_exec_info.callbacks != Py_None(&_Py_NoneStruct) && | ||||
3634 | PyList_Size(op_exec_info.callbacks) > 0; | ||||
3635 | op_exec_info.run_callbacks = op_exec_info.run_gradient_callback || | ||||
3636 | op_exec_info.run_post_exec_callbacks; | ||||
3637 | |||||
3638 | TF_Status* status = GetStatus(); | ||||
3639 | const char* op_name = TFE_GetPythonString(op_exec_info.op_name); | ||||
3640 | if (op_name == nullptr) { | ||||
3641 | PyErr_SetString(PyExc_TypeError, | ||||
3642 | Printf("expected a string for op_name, got %s instead", | ||||
3643 | op_exec_info.op_name->ob_type->tp_name) | ||||
3644 | .c_str()); | ||||
3645 | return nullptr; | ||||
3646 | } | ||||
3647 | |||||
3648 | TFE_Op* op = GetOp(ctx, op_name, op_exec_info.device_name, status); | ||||
3649 | |||||
3650 | auto cleaner = tensorflow::gtl::MakeCleanup([status, ctx, op] { | ||||
3651 | ReturnStatus(status); | ||||
3652 | ReturnOp(ctx, op); | ||||
3653 | }); | ||||
3654 | |||||
3655 | if (MaybeRaiseExceptionFromTFStatus(status, nullptr)) { | ||||
3656 | return nullptr; | ||||
3657 | } | ||||
3658 | |||||
3659 | tensorflow::unwrap(op)->SetStackTrace(tensorflow::GetStackTrace( | ||||
3660 | tensorflow::StackTrace::kStackTraceInitialSize)); | ||||
3661 | |||||
3662 | const tensorflow::OpDef* op_def = tensorflow::unwrap(op)->OpDef(); | ||||
3663 | if (op_def == nullptr) return nullptr; | ||||
3664 | |||||
3665 | if (args_size < | ||||
3666 | FAST_PATH_EXECUTE_ARG_INPUT_START + op_def->input_arg_size()) { | ||||
3667 | PyErr_SetString( | ||||
3668 | PyExc_ValueError, | ||||
3669 | Printf("Tuple size smaller than intended. Expected to be at least %d, " | ||||
3670 | "was %ld", | ||||
3671 | FAST_PATH_EXECUTE_ARG_INPUT_START + op_def->input_arg_size(), | ||||
3672 | args_size) | ||||
3673 | .c_str()); | ||||
3674 | return nullptr; | ||||
3675 | } | ||||
3676 | |||||
3677 | if (!CheckInputsOk(args, FAST_PATH_EXECUTE_ARG_INPUT_START, *op_def)) { | ||||
3678 | RaiseFallbackException( | ||||
3679 | "This function does not handle the case of the path where " | ||||
3680 | "all inputs are not already EagerTensors."); | ||||
3681 | return nullptr; | ||||
3682 | } | ||||
3683 | |||||
3684 | op_exec_info.attr_to_inputs_map = GetAttrToInputsMapHoldingGIL(*op_def); | ||||
3685 | op_exec_info.default_dtypes = GetAttrToDefaultsMapHoldingGIL(*op_def); | ||||
3686 | |||||
3687 | // Mapping of attr name to size - used to calculate the number of values | ||||
3688 | // to be expected by the TFE_Execute run. | ||||
3689 | tensorflow::gtl::FlatMap<string, int64_t> attr_list_sizes; | ||||
3690 | |||||
3691 | // Set non-inferred attrs, including setting defaults if the attr is passed in | ||||
3692 | // as None. | ||||
3693 | for (int i = FAST_PATH_EXECUTE_ARG_INPUT_START + op_def->input_arg_size(); | ||||
3694 | i < args_size; i += 2) { | ||||
3695 | PyObject* py_attr_name = PyTuple_GET_ITEM(args, i)(((static_cast<void> (0)), (PyTupleObject *)(args))-> ob_item[i]); | ||||
3696 | const char* attr_name = TFE_GetPythonString(py_attr_name); | ||||
3697 | PyObject* py_attr_value = PyTuple_GET_ITEM(args, i + 1)(((static_cast<void> (0)), (PyTupleObject *)(args))-> ob_item[i + 1]); | ||||
3698 | |||||
3699 | // Not creating an index since most of the time there are not more than a | ||||
3700 | // few attrs. | ||||
3701 | // TODO(nareshmodi): Maybe include the index as part of the | ||||
3702 | // OpRegistrationData. | ||||
3703 | for (const auto& attr : op_def->attr()) { | ||||
3704 | if (tensorflow::StringPiece(attr_name) == attr.name()) { | ||||
3705 | SetOpAttrWithDefaults(ctx, op, attr, attr_name, py_attr_value, | ||||
3706 | &attr_list_sizes, status); | ||||
3707 | |||||
3708 | if (!status->status.ok()) { | ||||
3709 | VLOG(1)(__builtin_expect(!!(!(([](int level, const char* fname) { static const bool vmodule_activated = ::tensorflow::internal::LogMessage ::VmoduleActivated(fname, level); return vmodule_activated; } )(1, "tensorflow/python/eager/pywrap_tfe_src.cc"))), 1)) ? (void )0 : ::tensorflow::internal::Voidifier() & ::tensorflow:: internal::LogMessage("tensorflow/python/eager/pywrap_tfe_src.cc" , 3709, tensorflow::INFO) << "Falling back to slow path for Op \"" << op_def->name() | ||||
3710 | << "\" since we are unable to set the value for attr \"" | ||||
3711 | << attr.name() << "\" due to: " << TF_Message(status); | ||||
3712 | RaiseFallbackException(TF_Message(status)); | ||||
3713 | return nullptr; | ||||
3714 | } | ||||
3715 | |||||
3716 | break; | ||||
3717 | } | ||||
3718 | } | ||||
3719 | } | ||||
3720 | |||||
3721 | // Flat attrs and inputs as required by the record_gradient call. The attrs | ||||
3722 | // here only contain inferred attrs (non-inferred attrs are added directly | ||||
3723 | // from the input args). | ||||
3724 | // All items in flattened_attrs and flattened_inputs contain | ||||
3725 | // Safe_PyObjectPtr - any time something steals a reference to this, it must | ||||
3726 | // INCREF. | ||||
3727 | // TODO(nareshmodi): figure out why PyList_New/PyList_Append don't work | ||||
3728 | // directly. | ||||
3729 | std::unique_ptr<std::vector<tensorflow::Safe_PyObjectPtr>> flattened_attrs = | ||||
3730 | nullptr; | ||||
3731 | std::unique_ptr<std::vector<tensorflow::Safe_PyObjectPtr>> flattened_inputs = | ||||
3732 | nullptr; | ||||
3733 | |||||
3734 | // TODO(nareshmodi): Encapsulate callbacks information into a struct. | ||||
3735 | if (op_exec_info.run_callbacks) { | ||||
3736 | flattened_attrs.reset(new std::vector<tensorflow::Safe_PyObjectPtr>); | ||||
3737 | flattened_inputs.reset(new std::vector<tensorflow::Safe_PyObjectPtr>); | ||||
3738 | } | ||||
3739 | |||||
3740 | // Add inferred attrs and inputs. | ||||
3741 | // The following code might set duplicate type attrs. This will result in | ||||
3742 | // the CacheKey for the generated AttrBuilder possibly differing from | ||||
3743 | // those where the type attrs are correctly set. Inconsistent CacheKeys | ||||
3744 | // for ops means that there might be unnecessarily duplicated kernels. | ||||
3745 | // TODO(nareshmodi): Fix this. | ||||
3746 | for (int i = 0; i < op_def->input_arg_size(); i++) { | ||||
3747 | const auto& input_arg = op_def->input_arg(i); | ||||
3748 | |||||
3749 | PyObject* input = | ||||
3750 | PyTuple_GET_ITEM(args, FAST_PATH_EXECUTE_ARG_INPUT_START + i)(((static_cast<void> (0)), (PyTupleObject *)(args))-> ob_item[FAST_PATH_EXECUTE_ARG_INPUT_START + i]); | ||||
3751 | if (!input_arg.number_attr().empty()) { | ||||
3752 | // The item is a homogeneous list. | ||||
3753 | if (!RaiseIfNotPySequence(input, input_arg.number_attr())) return nullptr; | ||||
3754 | tensorflow::Safe_PyObjectPtr fast_input( | ||||
3755 | PySequence_Fast(input, "Could not parse sequence.")); | ||||
3756 | if (fast_input.get() == nullptr) { | ||||
3757 | return nullptr; | ||||
3758 | } | ||||
3759 | Py_ssize_t len = PySequence_Fast_GET_SIZE(fast_input.get())(((((((PyObject*)(fast_input.get()))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((static_cast<void> (0)), (((PyVarObject*)(fast_input.get()))->ob_size)) : (( (PyVarObject*)(((static_cast<void> (0)), (PyTupleObject *)(fast_input.get()))))->ob_size)); | ||||
3760 | PyObject** fast_input_array = PySequence_Fast_ITEMS(fast_input.get())(((((((PyObject*)(fast_input.get()))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((PyListObject *)(fast_input .get()))->ob_item : ((PyTupleObject *)(fast_input.get()))-> ob_item); | ||||
3761 | |||||
3762 | TFE_OpSetAttrInt(op, input_arg.number_attr().data(), len); | ||||
3763 | if (op_exec_info.run_callbacks) { | ||||
3764 | flattened_attrs->emplace_back( | ||||
3765 | GetPythonObjectFromString(input_arg.number_attr())); | ||||
3766 | flattened_attrs->emplace_back(PyLong_FromLong(len)); | ||||
3767 | } | ||||
3768 | attr_list_sizes[input_arg.number_attr()] = len; | ||||
3769 | |||||
3770 | if (len > 0) { | ||||
3771 | // First item adds the type attr. | ||||
3772 | if (!AddInputToOp(&op_exec_info, fast_input_array[0], true, input_arg, | ||||
3773 | flattened_attrs.get(), flattened_inputs.get(), op, | ||||
3774 | status)) { | ||||
3775 | return nullptr; | ||||
3776 | } | ||||
3777 | |||||
3778 | for (Py_ssize_t j = 1; j < len; j++) { | ||||
3779 | // Since the list is homogeneous, we don't need to re-add the attr. | ||||
3780 | if (!AddInputToOp(&op_exec_info, fast_input_array[j], false, | ||||
3781 | input_arg, nullptr /* flattened_attrs */, | ||||
3782 | flattened_inputs.get(), op, status)) { | ||||
3783 | return nullptr; | ||||
3784 | } | ||||
3785 | } | ||||
3786 | } | ||||
3787 | } else if (!input_arg.type_list_attr().empty()) { | ||||
3788 | // The item is a heterogeneous list. | ||||
3789 | if (!RaiseIfNotPySequence(input, input_arg.type_list_attr())) { | ||||
3790 | return nullptr; | ||||
3791 | } | ||||
3792 | tensorflow::Safe_PyObjectPtr fast_input( | ||||
3793 | PySequence_Fast(input, "Could not parse sequence.")); | ||||
3794 | if (fast_input.get() == nullptr) { | ||||
3795 | return nullptr; | ||||
3796 | } | ||||
3797 | const string& attr_name = input_arg.type_list_attr(); | ||||
3798 | Py_ssize_t len = PySequence_Fast_GET_SIZE(fast_input.get())(((((((PyObject*)(fast_input.get()))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((static_cast<void> (0)), (((PyVarObject*)(fast_input.get()))->ob_size)) : (( (PyVarObject*)(((static_cast<void> (0)), (PyTupleObject *)(fast_input.get()))))->ob_size)); | ||||
3799 | PyObject** fast_input_array = PySequence_Fast_ITEMS(fast_input.get())(((((((PyObject*)(fast_input.get()))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((PyListObject *)(fast_input .get()))->ob_item : ((PyTupleObject *)(fast_input.get()))-> ob_item); | ||||
3800 | tensorflow::gtl::InlinedVector<TF_DataType, 4> attr_value(len); | ||||
3801 | PyObject* py_attr_value = nullptr; | ||||
3802 | if (op_exec_info.run_callbacks) { | ||||
3803 | py_attr_value = PyTuple_New(len); | ||||
3804 | } | ||||
3805 | for (Py_ssize_t j = 0; j < len; j++) { | ||||
3806 | PyObject* py_input = fast_input_array[j]; | ||||
3807 | tensorflow::Safe_PyObjectPtr py_eager_tensor; | ||||
3808 | if (!ConvertToTensor( | ||||
3809 | op_exec_info, py_input, &py_eager_tensor, | ||||
3810 | []() { return tensorflow::DT_INVALID; }, | ||||
3811 | [](const tensorflow::DataType dtype) {}, status)) { | ||||
3812 | return nullptr; | ||||
3813 | } | ||||
3814 | |||||
3815 | TFE_TensorHandle* input_handle = | ||||
3816 | EagerTensor_Handle(py_eager_tensor.get()); | ||||
3817 | |||||
3818 | attr_value[j] = TFE_TensorHandleDataType(input_handle); | ||||
3819 | |||||
3820 | TFE_OpAddInput(op, input_handle, status); | ||||
3821 | if (MaybeRaiseExceptionFromTFStatus(status, nullptr)) { | ||||
3822 | return nullptr; | ||||
3823 | } | ||||
3824 | |||||
3825 | if (op_exec_info.run_callbacks) { | ||||
3826 | flattened_inputs->emplace_back(std::move(py_eager_tensor)); | ||||
3827 | |||||
3828 | PyTuple_SET_ITEM(py_attr_value, j, PyLong_FromLong(attr_value[j]))PyTuple_SetItem(py_attr_value, j, PyLong_FromLong(attr_value[ j])); | ||||
3829 | } | ||||
3830 | } | ||||
3831 | if (op_exec_info.run_callbacks) { | ||||
3832 | flattened_attrs->emplace_back(GetPythonObjectFromString(attr_name)); | ||||
3833 | flattened_attrs->emplace_back(py_attr_value); | ||||
3834 | } | ||||
3835 | TFE_OpSetAttrTypeList(op, attr_name.data(), attr_value.data(), | ||||
3836 | attr_value.size()); | ||||
3837 | attr_list_sizes[attr_name] = len; | ||||
3838 | } else { | ||||
3839 | // The item is a single item. | ||||
3840 | if (!AddInputToOp(&op_exec_info, input, true, input_arg, | ||||
3841 | flattened_attrs.get(), flattened_inputs.get(), op, | ||||
3842 | status)) { | ||||
3843 | return nullptr; | ||||
3844 | } | ||||
3845 | } | ||||
3846 | } | ||||
3847 | |||||
3848 | int64_t num_outputs = 0; | ||||
3849 | for (int i = 0; i < op_def->output_arg_size(); i++) { | ||||
3850 | const auto& output_arg = op_def->output_arg(i); | ||||
3851 | int64_t delta = 1; | ||||
3852 | if (!output_arg.number_attr().empty()) { | ||||
3853 | delta = attr_list_sizes[output_arg.number_attr()]; | ||||
3854 | } else if (!output_arg.type_list_attr().empty()) { | ||||
3855 | delta = attr_list_sizes[output_arg.type_list_attr()]; | ||||
3856 | } | ||||
3857 | if (delta < 0) { | ||||
3858 | RaiseFallbackException( | ||||
3859 | "Attributes suggest that the size of an output list is less than 0"); | ||||
3860 | return nullptr; | ||||
3861 | } | ||||
3862 | num_outputs += delta; | ||||
3863 | } | ||||
3864 | |||||
3865 | // If number of retvals is larger than int32, we error out. | ||||
3866 | if (static_cast<int64_t>(static_cast<int32_t>(num_outputs)) != num_outputs) { | ||||
3867 | PyErr_SetString( | ||||
3868 | PyExc_ValueError, | ||||
3869 | Printf("Number of outputs is too big: %ld", num_outputs).c_str()); | ||||
3870 | return nullptr; | ||||
3871 | } | ||||
3872 | int num_retvals = num_outputs; | ||||
3873 | |||||
3874 | tensorflow::gtl::InlinedVector<TFE_TensorHandle*, 2> retvals(num_retvals); | ||||
3875 | |||||
3876 | Py_BEGIN_ALLOW_THREADS{ PyThreadState *_save; _save = PyEval_SaveThread();; | ||||
3877 | TFE_Execute(op, retvals.data(), &num_retvals, status); | ||||
3878 | Py_END_ALLOW_THREADSPyEval_RestoreThread(_save); }; | ||||
3879 | |||||
3880 | if (!status->status.ok()) { | ||||
3881 | // Augment the status with the op_name for easier debugging similar to | ||||
3882 | // TFE_Py_Execute. | ||||
3883 | status->status = tensorflow::errors::CreateWithUpdatedMessage( | ||||
3884 | status->status, tensorflow::strings::StrCat( | ||||
3885 | TF_Message(status), " [Op:", | ||||
3886 | TFE_GetPythonString(op_exec_info.op_name), "]")); | ||||
3887 | MaybeRaiseExceptionFromTFStatus(status, nullptr); | ||||
3888 | return nullptr; | ||||
3889 | } | ||||
3890 | |||||
3891 | tensorflow::Safe_PyObjectPtr flat_result(PyList_New(num_retvals)); | ||||
3892 | for (int i = 0; i < num_retvals; ++i) { | ||||
3893 | PyList_SET_ITEM(flat_result.get(), i, EagerTensorFromHandle(retvals[i]))PyList_SetItem(flat_result.get(), i, EagerTensorFromHandle(retvals [i])); | ||||
3894 | } | ||||
3895 | |||||
3896 | if (op_exec_info.run_callbacks) { | ||||
3897 | if (!RunCallbacks( | ||||
3898 | op_exec_info, args, | ||||
3899 | FAST_PATH_EXECUTE_ARG_INPUT_START + op_def->input_arg_size(), | ||||
3900 | *flattened_inputs, *flattened_attrs, flat_result.get())) { | ||||
3901 | return nullptr; | ||||
3902 | } | ||||
3903 | } | ||||
3904 | |||||
3905 | // Unflatten results. | ||||
3906 | if (op_def->output_arg_size() == 0) { | ||||
3907 | Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (& _Py_NoneStruct); | ||||
3908 | } | ||||
3909 | |||||
3910 | if (op_def->output_arg_size() == 1) { | ||||
3911 | if (!op_def->output_arg(0).number_attr().empty() || | ||||
3912 | !op_def->output_arg(0).type_list_attr().empty()) { | ||||
3913 | return flat_result.release(); | ||||
3914 | } else { | ||||
3915 | auto* result = PyList_GET_ITEM(flat_result.get(), 0)(((PyListObject *)(flat_result.get()))->ob_item[0]); | ||||
3916 | Py_INCREF(result)_Py_INCREF(((PyObject*)(result))); | ||||
3917 | return result; | ||||
3918 | } | ||||
3919 | } | ||||
3920 | |||||
3921 | // Correctly output the results that are made into a namedtuple. | ||||
3922 | PyObject* result = PyList_New(op_def->output_arg_size()); | ||||
3923 | int flat_result_index = 0; | ||||
3924 | for (int i = 0; i < op_def->output_arg_size(); i++) { | ||||
3925 | if (!op_def->output_arg(i).number_attr().empty()) { | ||||
3926 | int list_length = attr_list_sizes[op_def->output_arg(i).number_attr()]; | ||||
3927 | PyObject* inner_list = PyList_New(list_length); | ||||
3928 | for (int j = 0; j < list_length; j++) { | ||||
3929 | PyObject* obj = PyList_GET_ITEM(flat_result.get(), flat_result_index++)(((PyListObject *)(flat_result.get()))->ob_item[flat_result_index ++]); | ||||
3930 | Py_INCREF(obj)_Py_INCREF(((PyObject*)(obj))); | ||||
3931 | PyList_SET_ITEM(inner_list, j, obj)PyList_SetItem(inner_list, j, obj); | ||||
3932 | } | ||||
3933 | PyList_SET_ITEM(result, i, inner_list)PyList_SetItem(result, i, inner_list); | ||||
3934 | } else if (!op_def->output_arg(i).type_list_attr().empty()) { | ||||
3935 | int list_length = attr_list_sizes[op_def->output_arg(i).type_list_attr()]; | ||||
3936 | PyObject* inner_list = PyList_New(list_length); | ||||
3937 | for (int j = 0; j < list_length; j++) { | ||||
3938 | PyObject* obj = PyList_GET_ITEM(flat_result.get(), flat_result_index++)(((PyListObject *)(flat_result.get()))->ob_item[flat_result_index ++]); | ||||
3939 | Py_INCREF(obj)_Py_INCREF(((PyObject*)(obj))); | ||||
3940 | PyList_SET_ITEM(inner_list, j, obj)PyList_SetItem(inner_list, j, obj); | ||||
3941 | } | ||||
3942 | PyList_SET_ITEM(result, i, inner_list)PyList_SetItem(result, i, inner_list); | ||||
3943 | } else { | ||||
3944 | PyObject* obj = PyList_GET_ITEM(flat_result.get(), flat_result_index++)(((PyListObject *)(flat_result.get()))->ob_item[flat_result_index ++]); | ||||
3945 | Py_INCREF(obj)_Py_INCREF(((PyObject*)(obj))); | ||||
3946 | PyList_SET_ITEM(result, i, obj)PyList_SetItem(result, i, obj); | ||||
3947 | } | ||||
3948 | } | ||||
3949 | return result; | ||||
3950 | } | ||||
3951 | |||||
3952 | PyObject* TFE_Py_RecordGradient(PyObject* op_name, PyObject* inputs, | ||||
3953 | PyObject* attrs, PyObject* results, | ||||
3954 | PyObject* forward_pass_name_scope) { | ||||
3955 | if (*ThreadTapeIsStopped() || !HasAccumulatorOrTape()) { | ||||
3956 | Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (& _Py_NoneStruct); | ||||
3957 | } | ||||
3958 | |||||
3959 | return RecordGradient(op_name, inputs, attrs, results, | ||||
3960 | forward_pass_name_scope); | ||||
3961 | } | ||||
3962 | |||||
3963 | namespace { | ||||
3964 | const char kTensor[] = "T"; | ||||
3965 | const char kList[] = "L"; | ||||
3966 | const char kListEnd[] = "l"; | ||||
3967 | const char kTuple[] = "U"; | ||||
3968 | const char kTupleEnd[] = "u"; | ||||
3969 | const char kDIter[] = "I"; | ||||
3970 | const char kDict[] = "D"; | ||||
3971 | const char kRaw[] = "R"; | ||||
3972 | const char kResourceVariable[] = "r"; | ||||
3973 | const char kShape[] = "s"; | ||||
3974 | const char kShapeDelim[] = "-"; | ||||
3975 | const char kDType[] = "d"; | ||||
3976 | const char kNone[] = "n"; | ||||
3977 | const char kCompositeTensor[] = "C"; | ||||
3978 | const char kAttrs[] = "A"; | ||||
3979 | const char kAttrsEnd[] = "a"; | ||||
3980 | const char kName[] = "'"; | ||||
3981 | const char kNameEnd[] = "'"; | ||||
3982 | const char kLocalIdDelim[] = "_"; | ||||
3983 | |||||
3984 | // Container for storing generated string encoding as well as the raw python | ||||
3985 | // objects that were not included in the string. | ||||
3986 | struct EncodeResult { | ||||
3987 | string str; | ||||
3988 | std::vector<PyObject*> objects; | ||||
3989 | |||||
3990 | PyObject* ToPyTuple() { | ||||
3991 | PyObject* result = PyTuple_New(2); | ||||
3992 | |||||
3993 | PyTuple_SET_ITEM(result, 0, GetPythonObjectFromString(str))PyTuple_SetItem(result, 0, GetPythonObjectFromString(str)); | ||||
3994 | |||||
3995 | if (objects.empty()) { | ||||
3996 | Py_INCREF(Py_None)_Py_INCREF(((PyObject*)((&_Py_NoneStruct)))); | ||||
3997 | PyTuple_SET_ITEM(result, 1, Py_None)PyTuple_SetItem(result, 1, (&_Py_NoneStruct)); | ||||
3998 | } else { | ||||
3999 | PyObject* objects_tuple = PyTuple_New(objects.size()); | ||||
4000 | |||||
4001 | for (int i = 0; i < objects.size(); i++) { | ||||
4002 | PyTuple_SET_ITEM(objects_tuple, i, objects[i])PyTuple_SetItem(objects_tuple, i, objects[i]); | ||||
4003 | } | ||||
4004 | |||||
4005 | PyTuple_SET_ITEM(result, 1, objects_tuple)PyTuple_SetItem(result, 1, objects_tuple); | ||||
4006 | } | ||||
4007 | |||||
4008 | return result; | ||||
4009 | } | ||||
4010 | }; | ||||
4011 | |||||
4012 | // Gives each unique resource_id a unique incremental local_id. Provides a | ||||
4013 | // string encoding that informs an order and uniqueness sensitive input | ||||
4014 | // signature. | ||||
4015 | // This class is not thread safe and is not meant to be shared across threads. | ||||
4016 | class LocalResourceIdMap { | ||||
4017 | public: | ||||
4018 | // When the resource ID is known (such as for OwnedIterator). | ||||
4019 | // Returns the existing local ID (if present) or a new unique one. | ||||
4020 | int AddResourceId(int resource_id) { | ||||
4021 | const auto& it = resource_id_to_local_id_.find(resource_id); | ||||
4022 | if (it == resource_id_to_local_id_.end()) { | ||||
4023 | resource_id_to_local_id_[resource_id] = next_local_id_; | ||||
4024 | return next_local_id_++; | ||||
4025 | } else { | ||||
4026 | return it->second; | ||||
4027 | } | ||||
4028 | } | ||||
4029 | |||||
4030 | // When the resource ID is not known (such as for IteratorSpec). | ||||
4031 | // Returns a new unique local ID. | ||||
4032 | int AddUnknownResource() { return next_local_id_++; } | ||||
4033 | |||||
4034 | private: | ||||
4035 | absl::flat_hash_map<int, int> resource_id_to_local_id_; | ||||
4036 | int next_local_id_ = 0; | ||||
4037 | }; | ||||
4038 | |||||
4039 | // Contains encoding configuration, intermediary data and result. | ||||
4040 | struct EncodingContext { | ||||
4041 | bool include_tensor_ranks_only; | ||||
4042 | bool encode_variable_by_resource_id; | ||||
4043 | |||||
4044 | LocalResourceIdMap resource_id_map; | ||||
4045 | EncodeResult result; | ||||
4046 | }; | ||||
4047 | |||||
4048 | tensorflow::Status EncodeTensorOrTensorSpec(PyObject* arg, bool is_tensor_spec, | ||||
4049 | EncodingContext& context) { | ||||
4050 | absl::StrAppend(&context.result.str, kTensor); | ||||
4051 | |||||
4052 | if (is_tensor_spec) { | ||||
4053 | tensorflow::Safe_PyObjectPtr name(PyObject_GetAttrString(arg, "name")); | ||||
4054 | if (name != nullptr && name.get() != Py_None(&_Py_NoneStruct)) { | ||||
4055 | absl::StrAppend(&context.result.str, kName, | ||||
4056 | TFE_GetPythonString(name.get()), kNameEnd); | ||||
4057 | } | ||||
4058 | } | ||||
4059 | |||||
4060 | tensorflow::Safe_PyObjectPtr dtype_object( | ||||
4061 | PyObject_GetAttrString(arg, "dtype")); | ||||
4062 | if (dtype_object == nullptr) { | ||||
4063 | return tensorflow::errors::InvalidArgument( | ||||
4064 | "tf.TensorSpec object doesn't have dtype() attr."); | ||||
4065 | } | ||||
4066 | |||||
4067 | tensorflow::Safe_PyObjectPtr dtype_enum( | ||||
4068 | PyObject_GetAttrString(dtype_object.get(), "_type_enum")); | ||||
4069 | if (dtype_enum == nullptr) { | ||||
4070 | return tensorflow::errors::InvalidArgument( | ||||
4071 | "tf.TensorSpec's dtype object doesn't have _type_enum() " | ||||
4072 | "attr."); | ||||
4073 | } | ||||
4074 | |||||
4075 | tensorflow::DataType dtype = | ||||
4076 | static_cast<tensorflow::DataType>(MakeInt(dtype_enum.get())); | ||||
4077 | absl::StrAppend(&context.result.str, kDType, dtype); | ||||
4078 | |||||
4079 | tensorflow::Safe_PyObjectPtr shape_tuple( | ||||
4080 | PyObject_GetAttrString(arg, "shape")); | ||||
4081 | if (shape_tuple == nullptr) { | ||||
4082 | return tensorflow::errors::InvalidArgument( | ||||
4083 | "tf.TensorSpec object doesn't have shape() attr."); | ||||
4084 | } | ||||
4085 | |||||
4086 | tensorflow::Safe_PyObjectPtr rank( | ||||
4087 | PyObject_GetAttr(shape_tuple.get(), PyUnicode_FromString("rank"))); | ||||
4088 | if (rank == nullptr || rank.get() == Py_None(&_Py_NoneStruct)) { | ||||
4089 | // Unknown shape, encode that directly. | ||||
4090 | absl::StrAppend(&context.result.str, kNone); | ||||
4091 | return tensorflow::Status::OK(); | ||||
4092 | } | ||||
4093 | |||||
4094 | absl::StrAppend(&context.result.str, kShape); | ||||
4095 | |||||
4096 | tensorflow::Safe_PyObjectPtr shape_seq(PySequence_Fast( | ||||
4097 | shape_tuple.get(), "shape_tuple didn't return a sequence")); | ||||
4098 | |||||
4099 | int len = MakeInt(rank.get()); | ||||
4100 | PyObject** shape_seq_array = PySequence_Fast_ITEMS(shape_seq.get())(((((((PyObject*)(shape_seq.get()))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((PyListObject *)(shape_seq .get()))->ob_item : ((PyTupleObject *)(shape_seq.get()))-> ob_item); | ||||
4101 | |||||
4102 | if (context.include_tensor_ranks_only) { | ||||
4103 | absl::StrAppend(&context.result.str, len); | ||||
4104 | } else { | ||||
4105 | for (int i = 0; i < len; ++i) { | ||||
4106 | // Can be None, int or a Dimension object. | ||||
4107 | PyObject* dimension = shape_seq_array[i]; | ||||
4108 | |||||
4109 | // If it is a Dimension object, then we must extract value from it first. | ||||
4110 | bool is_dimension_class = PyObject_HasAttrString(dimension, "value"); | ||||
4111 | tensorflow::Safe_PyObjectPtr dimension_holder; | ||||
4112 | if (is_dimension_class) { | ||||
4113 | dimension_holder = | ||||
4114 | tensorflow::make_safe(PyObject_GetAttrString(dimension, "value")); | ||||
4115 | dimension = dimension_holder.get(); | ||||
4116 | } | ||||
4117 | |||||
4118 | if (dimension == Py_None(&_Py_NoneStruct)) { | ||||
4119 | absl::StrAppend(&context.result.str, kNone); | ||||
4120 | } else { | ||||
4121 | absl::StrAppend(&context.result.str, MakeInt(dimension), kShapeDelim); | ||||
4122 | } | ||||
4123 | } | ||||
4124 | } | ||||
4125 | |||||
4126 | return tensorflow::Status::OK(); | ||||
4127 | } | ||||
4128 | |||||
4129 | // TODO(b/199534088): Remove this function by using EncodeResource instead. | ||||
4130 | tensorflow::Status EncodeOwnedIterator(PyObject* arg, | ||||
4131 | EncodingContext& context) { | ||||
4132 | PyObject* type_spec(PyObject_GetAttrString(arg, "_type_spec")); | ||||
4133 | if (type_spec == nullptr) { | ||||
4134 | return tensorflow::errors::InvalidArgument( | ||||
4135 | "Error while reading OwnedIterator._type_spec."); | ||||
4136 | } | ||||
4137 | context.result.objects.push_back(type_spec); | ||||
4138 | |||||
4139 | // Add resource tracking | ||||
4140 | tensorflow::Safe_PyObjectPtr itr_res( | ||||
4141 | PyObject_GetAttrString(arg, "_iterator_resource")); | ||||
4142 | if (itr_res == nullptr) { | ||||
4143 | return tensorflow::errors::InvalidArgument( | ||||
4144 | "Error while reading Dataset iterator resource."); | ||||
4145 | } | ||||
4146 | // OwnedIterator should ideally always provide a unique resource id. | ||||
4147 | // TODO(b/199534088) Cases where resource_id is not provided need to be fixed. | ||||
4148 | if (tensorflow::swig::IsTensor(itr_res.get())) { | ||||
4149 | absl::StrAppend(&context.result.str, kDIter); | ||||
4150 | tensorflow::Safe_PyObjectPtr py_resource_id( | ||||
4151 | PyObject_GetAttrString(itr_res.get(), "_id")); | ||||
4152 | if (py_resource_id == nullptr) { | ||||
4153 | return tensorflow::errors::InvalidArgument( | ||||
4154 | "Error while reading Dataset iterator resouce id."); | ||||
4155 | } | ||||
4156 | int resource_id = PyLong_AsSize_t(py_resource_id.get()); | ||||
4157 | if (resource_id < 0) { | ||||
4158 | return tensorflow::errors::InvalidArgument("PyLong_AsSize_t failure"); | ||||
4159 | } | ||||
4160 | int local_id = context.resource_id_map.AddResourceId(resource_id); | ||||
4161 | absl::StrAppend(&context.result.str, local_id, kLocalIdDelim); | ||||
4162 | } else { | ||||
4163 | // If '_iterator_resource' is not a Tensor, there is no resource id. | ||||
4164 | // Instead we treat it the same way as a CompositeTensor | ||||
4165 | absl::StrAppend(&context.result.str, kCompositeTensor); | ||||
4166 | } | ||||
4167 | return tensorflow::Status::OK(); | ||||
4168 | } | ||||
4169 | |||||
4170 | tensorflow::Status EncodeResource(PyObject* arg, EncodingContext& context) { | ||||
4171 | absl::StrAppend(&context.result.str, kResourceVariable); | ||||
4172 | tensorflow::Safe_PyObjectPtr py_resource_id( | ||||
4173 | PyObject_CallMethod(arg, "__tf_resource_id__", nullptr)); | ||||
4174 | DCHECK(py_resource_id != nullptr)while (false && (py_resource_id != nullptr)) ::tensorflow ::internal::LogMessageFatal("tensorflow/python/eager/pywrap_tfe_src.cc" , 4174); | ||||
4175 | |||||
4176 | int resource_id = PyLong_AsSize_t(py_resource_id.get()); | ||||
4177 | DCHECK_GE(resource_id, 0)while (false && ((void)(resource_id), (void)(0), 0)) :: tensorflow::internal::LogMessageFatal("tensorflow/python/eager/pywrap_tfe_src.cc" , 4177); | ||||
4178 | int local_id = context.resource_id_map.AddResourceId(resource_id); | ||||
4179 | absl::StrAppend(&context.result.str, local_id, kLocalIdDelim); | ||||
4180 | |||||
4181 | tensorflow::Safe_PyObjectPtr type_spec( | ||||
4182 | PyObject_CallMethod(arg, "__tf_function_cache_spec__", nullptr)); | ||||
4183 | absl::StrAppend(&context.result.str, PyUnicode_AsUTF8(type_spec.get())); | ||||
4184 | DCHECK(type_spec != nullptr)while (false && (type_spec != nullptr)) ::tensorflow:: internal::LogMessageFatal("tensorflow/python/eager/pywrap_tfe_src.cc" , 4184); | ||||
4185 | |||||
4186 | return tensorflow::Status::OK(); | ||||
4187 | } | ||||
4188 | |||||
4189 | tensorflow::Status EncodeArgHelperInternal(PyObject* arg, | ||||
4190 | EncodingContext& context); | ||||
4191 | |||||
4192 | // This function doesn't set the type of sequence before | ||||
4193 | tensorflow::Status EncodeSequence(PyObject* arg, const char* type, | ||||
4194 | const char* end_type, | ||||
4195 | EncodingContext& context) { | ||||
4196 | tensorflow::Safe_PyObjectPtr arg_seq( | ||||
4197 | PySequence_Fast(arg, "unable to create seq from list/tuple")); | ||||
4198 | |||||
4199 | absl::StrAppend(&context.result.str, type); | ||||
4200 | int len = PySequence_Fast_GET_SIZE(arg_seq.get())(((((((PyObject*)(arg_seq.get()))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((static_cast<void> (0)), (((PyVarObject*)(arg_seq.get()))->ob_size)) : (((PyVarObject *)(((static_cast<void> (0)), (PyTupleObject *)(arg_seq. get()))))->ob_size)); | ||||
4201 | PyObject** arg_seq_array = PySequence_Fast_ITEMS(arg_seq.get())(((((((PyObject*)(arg_seq.get()))->ob_type))->tp_flags & ((1UL << 25))) != 0) ? ((PyListObject *)(arg_seq.get() ))->ob_item : ((PyTupleObject *)(arg_seq.get()))->ob_item ); | ||||
4202 | for (int i = 0; i < len; ++i) { | ||||
4203 | PyObject* item = arg_seq_array[i]; | ||||
4204 | if (item == Py_None(&_Py_NoneStruct)) { | ||||
4205 | absl::StrAppend(&context.result.str, kNone); | ||||
4206 | } else { | ||||
4207 | TF_RETURN_IF_ERROR(EncodeArgHelperInternal(item, context))do { ::tensorflow::Status _status = (EncodeArgHelperInternal( item, context)); if ((__builtin_expect(!_status.ok(), 0))) return _status; } while (0); | ||||
4208 | } | ||||
4209 | } | ||||
4210 | absl::StrAppend(&context.result.str, end_type); | ||||
4211 | |||||
4212 | return tensorflow::Status::OK(); | ||||
4213 | } | ||||
4214 | |||||
4215 | tensorflow::Status EncodeMapping(PyObject* arg, EncodingContext& context) { | ||||
4216 | tensorflow::Safe_PyObjectPtr keys(tensorflow::swig::MappingKeys(arg)); | ||||
4217 | if (PyList_Sort(keys.get()) == -1) { | ||||
4218 | return tensorflow::errors::Internal("Unable to sort keys"); | ||||
4219 | } | ||||
4220 | |||||
4221 | absl::StrAppend(&context.result.str, kDict); | ||||
4222 | int len = PyList_Size(keys.get()); | ||||
4223 | |||||
4224 | for (int i = 0; i < len; i++) { | ||||
4225 | PyObject* key = PyList_GetItem(keys.get(), i); | ||||
4226 | TF_RETURN_IF_ERROR(EncodeArgHelperInternal(key, context))do { ::tensorflow::Status _status = (EncodeArgHelperInternal( key, context)); if ((__builtin_expect(!_status.ok(), 0))) return _status; } while (0); | ||||
4227 | tensorflow::Safe_PyObjectPtr value(PyObject_GetItem(arg, key)); | ||||
4228 | TF_RETURN_IF_ERROR(EncodeArgHelperInternal(value.get(), context))do { ::tensorflow::Status _status = (EncodeArgHelperInternal( value.get(), context)); if ((__builtin_expect(!_status.ok(), 0 ))) return _status; } while (0); | ||||
4229 | } | ||||
4230 | |||||
4231 | return tensorflow::Status::OK(); | ||||
4232 | } | ||||
4233 | |||||
4234 | tensorflow::Status EncodeCompositeTensor(PyObject* arg, | ||||
4235 | EncodingContext& context) { | ||||
4236 | absl::StrAppend(&context.result.str, kCompositeTensor); | ||||
4237 | PyObject* type_spec(PyObject_GetAttrString(arg, "_type_spec")); | ||||
4238 | if (type_spec == nullptr) { | ||||
4239 | return tensorflow::errors::InvalidArgument( | ||||
4240 | "Error while reading CompositeTensor._type_spec."); | ||||
4241 | } | ||||
4242 | context.result.objects.push_back(type_spec); | ||||
4243 | |||||
4244 | return tensorflow::Status::OK(); | ||||
4245 | } | ||||
4246 | |||||
4247 | tensorflow::Status EncodeTypeSpec(PyObject* arg, EncodingContext& context) { | ||||
4248 | absl::StrAppend(&context.result.str, kRaw); | ||||
4249 | Py_INCREF(arg)_Py_INCREF(((PyObject*)(arg))); | ||||
4250 | context.result.objects.push_back(arg); | ||||
4251 | return tensorflow::Status::OK(); | ||||
4252 | } | ||||
4253 | |||||
4254 | tensorflow::Status EncodeAttrs(PyObject* arg, EncodingContext& context) { | ||||
4255 | absl::StrAppend(&context.result.str, kAttrs); | ||||
4256 | tensorflow::Safe_PyObjectPtr attrs( | ||||
4257 | PyObject_GetAttrString(arg, "__attrs_attrs__")); | ||||
4258 | tensorflow::Safe_PyObjectPtr iter(PyObject_GetIter(attrs.get())); | ||||
4259 | for (tensorflow::Safe_PyObjectPtr item(PyIter_Next(iter.get())); item; | ||||
4260 | item.reset(PyIter_Next(iter.get()))) { | ||||
4261 | tensorflow::Safe_PyObjectPtr name( | ||||
4262 | PyObject_GetAttrString(item.get(), "name")); | ||||
4263 | tensorflow::Safe_PyObjectPtr attr_arg(PyObject_GetAttr(arg, name.get())); | ||||
4264 | TF_RETURN_IF_ERROR(EncodeArgHelperInternal(attr_arg.get(), context))do { ::tensorflow::Status _status = (EncodeArgHelperInternal( attr_arg.get(), context)); if ((__builtin_expect(!_status.ok( ), 0))) return _status; } while (0); | ||||
4265 | } | ||||
4266 | absl::StrAppend(&context.result.str, kAttrsEnd); | ||||
4267 | |||||
4268 | return tensorflow::Status::OK(); | ||||
4269 | } | ||||
4270 | |||||
4271 | tensorflow::Status EncodeUnidentified(PyObject* arg, EncodingContext& context) { | ||||
4272 | // We hold a weak reference because cache keys live practically forever, and | ||||
4273 | // this may leak heavy objects. | ||||
4274 | PyObject* object = PyWeakref_NewRef(arg, nullptr); | ||||
4275 | if (object == nullptr) { | ||||
4276 | PyErr_Clear(); | ||||
4277 | object = arg; | ||||
4278 | Py_INCREF(object)_Py_INCREF(((PyObject*)(object))); | ||||
4279 | } | ||||
4280 | |||||
4281 | absl::StrAppend(&context.result.str, kRaw); | ||||
4282 | context.result.objects.push_back(object); | ||||
4283 | return tensorflow::Status::OK(); | ||||
4284 | } | ||||
4285 | |||||
4286 | tensorflow::Status EncodeArgHelperInternal(PyObject* arg, | ||||
4287 | EncodingContext& context) { | ||||
4288 | if (tensorflow::swig::IsTensorSpec(arg)) { | ||||
4289 | TF_RETURN_IF_ERROR(EncodeTensorOrTensorSpec(arg, true, context))do { ::tensorflow::Status _status = (EncodeTensorOrTensorSpec (arg, true, context)); if ((__builtin_expect(!_status.ok(), 0 ))) return _status; } while (0); | ||||
4290 | } else if (tensorflow::swig::IsTensor(arg)) { | ||||
4291 | TF_RETURN_IF_ERROR(EncodeTensorOrTensorSpec(arg, false, context))do { ::tensorflow::Status _status = (EncodeTensorOrTensorSpec (arg, false, context)); if ((__builtin_expect(!_status.ok(), 0 ))) return _status; } while (0); | ||||
4292 | } else if (tensorflow::swig::IsOwnedIterator(arg)) { | ||||
4293 | TF_RETURN_IF_ERROR(EncodeOwnedIterator(arg, context))do { ::tensorflow::Status _status = (EncodeOwnedIterator(arg, context)); if ((__builtin_expect(!_status.ok(), 0))) return _status ; } while (0); | ||||
4294 | } else if (PyList_Check(arg)((((((PyObject*)(arg))->ob_type))->tp_flags & ((1UL << 25))) != 0)) { | ||||
4295 | TF_RETURN_IF_ERROR(EncodeSequence(arg, kList, kListEnd, context))do { ::tensorflow::Status _status = (EncodeSequence(arg, kList , kListEnd, context)); if ((__builtin_expect(!_status.ok(), 0 ))) return _status; } while (0); | ||||
4296 | } else if (tensorflow::swig::IsTuple(arg)) { | ||||
4297 | TF_RETURN_IF_ERROR(EncodeSequence(arg, kTuple, kTupleEnd, context))do { ::tensorflow::Status _status = (EncodeSequence(arg, kTuple , kTupleEnd, context)); if ((__builtin_expect(!_status.ok(), 0 ))) return _status; } while (0); | ||||
4298 | } else if (tensorflow::swig::IsMapping(arg)) { | ||||
4299 | TF_RETURN_IF_ERROR(EncodeMapping(arg, context))do { ::tensorflow::Status _status = (EncodeMapping(arg, context )); if ((__builtin_expect(!_status.ok(), 0))) return _status; } while (0); | ||||
4300 | } else if (tensorflow::swig::IsCompositeTensor(arg)) { | ||||
4301 | TF_RETURN_IF_ERROR(EncodeCompositeTensor(arg, context))do { ::tensorflow::Status _status = (EncodeCompositeTensor(arg , context)); if ((__builtin_expect(!_status.ok(), 0))) return _status; } while (0); | ||||
4302 | } else if (tensorflow::swig::IsTypeSpec(arg)) { | ||||
4303 | TF_RETURN_IF_ERROR(EncodeTypeSpec(arg, context))do { ::tensorflow::Status _status = (EncodeTypeSpec(arg, context )); if ((__builtin_expect(!_status.ok(), 0))) return _status; } while (0); | ||||
4304 | } else if (tensorflow::swig::IsAttrs(arg)) { | ||||
4305 | TF_RETURN_IF_ERROR(EncodeAttrs(arg, context))do { ::tensorflow::Status _status = (EncodeAttrs(arg, context )); if ((__builtin_expect(!_status.ok(), 0))) return _status; } while (0); | ||||
4306 | } else if (tensorflow::swig::IsResourceVariable(arg) && | ||||
4307 | context.encode_variable_by_resource_id) { | ||||
4308 | TF_RETURN_IF_ERROR(EncodeResource(arg, context))do { ::tensorflow::Status _status = (EncodeResource(arg, context )); if ((__builtin_expect(!_status.ok(), 0))) return _status; } while (0); | ||||
4309 | } else { | ||||
4310 | TF_RETURN_IF_ERROR(EncodeUnidentified(arg, context))do { ::tensorflow::Status _status = (EncodeUnidentified(arg, context )); if ((__builtin_expect(!_status.ok(), 0))) return _status; } while (0); | ||||
4311 | } | ||||
4312 | |||||
4313 | return tensorflow::Status::OK(); | ||||
4314 | } | ||||
4315 | |||||
4316 | tensorflow::Status TFE_Py_EncodeArgHelper(PyObject* arg, | ||||
4317 | EncodingContext& context) { | ||||
4318 | auto status = EncodeArgHelperInternal(arg, context); | ||||
4319 | return status; | ||||
4320 | } | ||||
4321 | |||||
4322 | } // namespace | ||||
4323 | |||||
4324 | // `defun` uses dtypes and shapes instead of `Tensors` as cache keys. Dtypes | ||||
4325 | // are used because TensorFlow graphs are not parametric w.r.t. dtypes. Shapes | ||||
4326 | // are used for both performance reasons, as much TensorFlow code specializes | ||||
4327 | // on known shapes to produce slimmer graphs, and correctness, as some | ||||
4328 | // high-level APIs require shapes to be fully-known. | ||||
4329 | // | ||||
4330 | // `include_tensor_ranks_only` allows caching on arguments excluding shape info, | ||||
4331 | // so that a slow path using relaxed shape can rely on a cache key that excludes | ||||
4332 | // shapes. | ||||
4333 | PyObject* TFE_Py_EncodeArg(PyObject* arg, bool include_tensor_ranks_only, | ||||
4334 | bool encode_variable_by_resource_id) { | ||||
4335 | EncodingContext context; | ||||
4336 | context.include_tensor_ranks_only = include_tensor_ranks_only; | ||||
4337 | context.encode_variable_by_resource_id = encode_variable_by_resource_id; | ||||
4338 | const auto status = TFE_Py_EncodeArgHelper(arg, context); | ||||
4339 | if (MaybeRaiseExceptionFromStatus(status, nullptr)) { | ||||
4340 | return nullptr; | ||||
4341 | } | ||||
4342 | |||||
4343 | return context.result.ToPyTuple(); | ||||
4344 | } | ||||
4345 | |||||
4346 | // A method prints incoming messages directly to Python's | ||||
4347 | // stdout using Python's C API. This is necessary in Jupyter notebooks | ||||
4348 | // and colabs where messages to the C stdout don't go to the notebook | ||||
4349 | // cell outputs, but calls to Python's stdout do. | ||||
4350 | void PrintToPythonStdout(const char* msg) { | ||||
4351 | if (Py_IsInitialized()) { | ||||
4352 | PyGILState_STATE py_threadstate; | ||||
4353 | py_threadstate = PyGILState_Ensure(); | ||||
4354 | |||||
4355 | string string_msg = msg; | ||||
4356 | // PySys_WriteStdout truncates strings over 1000 bytes, so | ||||
4357 | // we write the message in chunks small enough to not be truncated. | ||||
4358 | int CHUNK_SIZE = 900; | ||||
4359 | auto len = string_msg.length(); | ||||
4360 | for (int i = 0; i < len; i += CHUNK_SIZE) { | ||||
4361 | PySys_WriteStdout("%s", string_msg.substr(i, CHUNK_SIZE).c_str()); | ||||
4362 | } | ||||
4363 | |||||
4364 | // Force flushing to make sure print newlines aren't interleaved in | ||||
4365 | // some colab environments | ||||
4366 | PyRun_SimpleString("import sys; sys.stdout.flush()")PyRun_SimpleStringFlags("import sys; sys.stdout.flush()", __null ); | ||||
4367 | |||||
4368 | PyGILState_Release(py_threadstate); | ||||
4369 | } | ||||
4370 | } | ||||
4371 | |||||
4372 | // Register PrintToPythonStdout as a log listener, to allow | ||||
4373 | // printing in colabs and jupyter notebooks to work. | ||||
4374 | void TFE_Py_EnableInteractivePythonLogging() { | ||||
4375 | static bool enabled_interactive_logging = false; | ||||
4376 | if (!enabled_interactive_logging) { | ||||
4377 | enabled_interactive_logging = true; | ||||
4378 | TF_RegisterLogListener(PrintToPythonStdout); | ||||
4379 | } | ||||
4380 | } | ||||
4381 | |||||
4382 | namespace { | ||||
4383 | // weak reference to Python Context object currently active | ||||
4384 | PyObject* weak_eager_context = nullptr; | ||||
4385 | } // namespace | ||||
4386 | |||||
4387 | PyObject* TFE_Py_SetEagerContext(PyObject* py_context) { | ||||
4388 | Py_XDECREF(weak_eager_context)_Py_XDECREF(((PyObject*)(weak_eager_context))); | ||||
4389 | weak_eager_context = PyWeakref_NewRef(py_context, nullptr); | ||||
4390 | if (weak_eager_context == nullptr) { | ||||
4391 | return nullptr; | ||||
4392 | } | ||||
4393 | Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (& _Py_NoneStruct); | ||||
4394 | } | ||||
4395 | |||||
4396 | PyObject* GetPyEagerContext() { | ||||
4397 | if (weak_eager_context == nullptr) { | ||||
4398 | PyErr_SetString(PyExc_RuntimeError, "Python eager context is not set"); | ||||
4399 | return nullptr; | ||||
4400 | } | ||||
4401 | PyObject* py_context = PyWeakref_GET_OBJECT(weak_eager_context)((((PyObject*)(((PyWeakReference *)(weak_eager_context))-> wr_object))->ob_refcnt) > 0 ? ((PyWeakReference *)(weak_eager_context ))->wr_object : (&_Py_NoneStruct)); | ||||
4402 | if (py_context == Py_None(&_Py_NoneStruct)) { | ||||
4403 | PyErr_SetString(PyExc_RuntimeError, "Eager context has been destroyed"); | ||||
4404 | return nullptr; | ||||
4405 | } | ||||
4406 | Py_INCREF(py_context)_Py_INCREF(((PyObject*)(py_context))); | ||||
4407 | return py_context; | ||||
4408 | } | ||||
4409 | |||||
4410 | namespace { | ||||
4411 | |||||
4412 | // Default values for thread_local_data fields. | ||||
4413 | struct EagerContextThreadLocalDataDefaults { | ||||
4414 | tensorflow::Safe_PyObjectPtr is_eager; | ||||
4415 | tensorflow::Safe_PyObjectPtr device_spec; | ||||
4416 | }; | ||||
4417 | |||||
4418 | // Maps each py_eager_context object to its thread_local_data. | ||||
4419 | // | ||||
4420 | // Note: we need to use the python Context object as the key here (and not | ||||
4421 | // its handle object), because the handle object isn't created until the | ||||
4422 | // context is initialized; but thread_local_data is potentially accessed | ||||
4423 | // before then. | ||||
4424 | using EagerContextThreadLocalDataMap = absl::flat_hash_map< | ||||
4425 | PyObject*, std::unique_ptr<tensorflow::EagerContextThreadLocalData>>; | ||||
4426 | thread_local EagerContextThreadLocalDataMap* | ||||
4427 | eager_context_thread_local_data_map = nullptr; | ||||
4428 | |||||
4429 | // Maps each py_eager_context object to default values. | ||||
4430 | using EagerContextThreadLocalDataDefaultsMap = | ||||
4431 | absl::flat_hash_map<PyObject*, EagerContextThreadLocalDataDefaults>; | ||||
4432 | EagerContextThreadLocalDataDefaultsMap* | ||||
4433 | eager_context_thread_local_data_defaults = nullptr; | ||||
4434 | |||||
4435 | } // namespace | ||||
4436 | |||||
4437 | namespace tensorflow { | ||||
4438 | |||||
4439 | void MakeEagerContextThreadLocalData(PyObject* py_eager_context, | ||||
4440 | PyObject* is_eager, | ||||
4441 | PyObject* device_spec) { | ||||
4442 | DCheckPyGilState(); | ||||
4443 | if (eager_context_thread_local_data_defaults == nullptr) { | ||||
4444 | absl::LeakCheckDisabler disabler; | ||||
4445 | eager_context_thread_local_data_defaults = | ||||
4446 | new EagerContextThreadLocalDataDefaultsMap(); | ||||
4447 | } | ||||
4448 | if (eager_context_thread_local_data_defaults->count(py_eager_context) > 0) { | ||||
4449 | PyErr_SetString(PyExc_AssertionError, | ||||
4450 | "MakeEagerContextThreadLocalData may not be called " | ||||
4451 | "twice on the same eager Context object."); | ||||
4452 | } | ||||
4453 | |||||
4454 | auto& defaults = | ||||
4455 | (*eager_context_thread_local_data_defaults)[py_eager_context]; | ||||
4456 | Py_INCREF(is_eager)_Py_INCREF(((PyObject*)(is_eager))); | ||||
4457 | defaults.is_eager.reset(is_eager); | ||||
4458 | Py_INCREF(device_spec)_Py_INCREF(((PyObject*)(device_spec))); | ||||
4459 | defaults.device_spec.reset(device_spec); | ||||
4460 | } | ||||
4461 | |||||
4462 | EagerContextThreadLocalData* GetEagerContextThreadLocalData( | ||||
4463 | PyObject* py_eager_context) { | ||||
4464 | if (eager_context_thread_local_data_defaults == nullptr) { | ||||
4465 | PyErr_SetString(PyExc_AssertionError, | ||||
4466 | "MakeEagerContextThreadLocalData must be called " | ||||
4467 | "before GetEagerContextThreadLocalData."); | ||||
4468 | return nullptr; | ||||
4469 | } | ||||
4470 | auto defaults = | ||||
4471 | eager_context_thread_local_data_defaults->find(py_eager_context); | ||||
4472 | if (defaults == eager_context_thread_local_data_defaults->end()) { | ||||
4473 | PyErr_SetString(PyExc_AssertionError, | ||||
4474 | "MakeEagerContextThreadLocalData must be called " | ||||
4475 | "before GetEagerContextThreadLocalData."); | ||||
4476 | return nullptr; | ||||
4477 | } | ||||
4478 | |||||
4479 | if (eager_context_thread_local_data_map == nullptr) { | ||||
4480 | absl::LeakCheckDisabler disabler; | ||||
4481 | eager_context_thread_local_data_map = new EagerContextThreadLocalDataMap(); | ||||
4482 | } | ||||
4483 | auto& thread_local_data = | ||||
4484 | (*eager_context_thread_local_data_map)[py_eager_context]; | ||||
4485 | |||||
4486 | if (!thread_local_data) { | ||||
4487 | thread_local_data.reset(new EagerContextThreadLocalData()); | ||||
4488 | |||||
4489 | Safe_PyObjectPtr is_eager( | ||||
4490 | PyObject_CallFunctionObjArgs(defaults->second.is_eager.get(), nullptr)); | ||||
4491 | if (!is_eager) return nullptr; | ||||
4492 | thread_local_data->is_eager = PyObject_IsTrue(is_eager.get()); | ||||
4493 | |||||
4494 | #if PY_MAJOR_VERSION3 >= 3 | ||||
4495 | PyObject* scope_name = PyUnicode_FromString(""); | ||||
4496 | #else | ||||
4497 | PyObject* scope_name = PyString_FromString(""); | ||||
4498 | #endif | ||||
4499 | thread_local_data->scope_name.reset(scope_name); | ||||
4500 | |||||
4501 | #if PY_MAJOR_VERSION3 >= 3 | ||||
4502 | PyObject* device_name = PyUnicode_FromString(""); | ||||
4503 | #else | ||||
4504 | PyObject* device_name = PyString_FromString(""); | ||||
4505 | #endif | ||||
4506 | thread_local_data->device_name.reset(device_name); | ||||
4507 | |||||
4508 | Py_INCREF(defaults->second.device_spec.get())_Py_INCREF(((PyObject*)(defaults->second.device_spec.get() ))); | ||||
4509 | thread_local_data->device_spec.reset(defaults->second.device_spec.get()); | ||||
4510 | |||||
4511 | Py_INCREF(Py_None)_Py_INCREF(((PyObject*)((&_Py_NoneStruct)))); | ||||
4512 | thread_local_data->function_call_options.reset(Py_None(&_Py_NoneStruct)); | ||||
4513 | |||||
4514 | Py_INCREF(Py_None)_Py_INCREF(((PyObject*)((&_Py_NoneStruct)))); | ||||
4515 | thread_local_data->executor.reset(Py_None(&_Py_NoneStruct)); | ||||
4516 | |||||
4517 | thread_local_data->op_callbacks.reset(PyList_New(0)); | ||||
4518 | } | ||||
4519 | return thread_local_data.get(); | ||||
4520 | } | ||||
4521 | |||||
4522 | void DestroyEagerContextThreadLocalData(PyObject* py_eager_context) { | ||||
4523 | DCheckPyGilState(); | ||||
4524 | if (eager_context_thread_local_data_defaults) { | ||||
4525 | eager_context_thread_local_data_defaults->erase(py_eager_context); | ||||
4526 | } | ||||
4527 | if (eager_context_thread_local_data_map) { | ||||
4528 | eager_context_thread_local_data_map->erase(py_eager_context); | ||||
4529 | } | ||||
4530 | } | ||||
4531 | |||||
4532 | } // namespace tensorflow |
1 | #ifndef PyObject_GetAttrString |
2 | struct _object; |
3 | typedef struct _object PyObject; |
4 | PyObject* clang_analyzer_PyObject_New_Reference(); |
5 | PyObject* PyObject_GetAttrString(PyObject *o, const char *attr_name) { |
6 | return clang_analyzer_PyObject_New_Reference(); |
7 | } |
8 | #else |
9 | #warning "API PyObject_GetAttrString is defined as a macro." |
10 | #endif |