Calls the registered forward_gradient_function, computing `output_tangents` from `input_tangents`. `output_tangents` must not be null. `op_name`, `attrs`, `inputs`, and `results` describe the operation for which the forward function is being called.
| 2431 | // `op_name`, `attrs`, `inputs`, and `results` describe the operation for which |
| 2432 | // the forward function is being called. |
| 2433 | tensorflow::Status CallForwardGradientFunction( |
| 2434 | PyObject* op_name, PyObject* attrs, PyObject* inputs, PyObject* results, |
| 2435 | const std::vector<PyObject*>& input_tangents, |
| 2436 | std::vector<PyObject*>* output_tangents) { |
| 2437 | if (forward_gradient_function == nullptr) { |
| 2438 | return tensorflow::errors::Internal( |
| 2439 | "No forward gradient function registered."); |
| 2440 | } |
| 2441 | tensorflow::Safe_PyObjectPtr py_input_tangents( |
| 2442 | PyTuple_New(input_tangents.size())); |
| 2443 | for (size_t i = 0; i < input_tangents.size(); ++i) { |
| 2444 | PyObject* element; |
| 2445 | if (input_tangents[i] == nullptr) { |
| 2446 | element = Py_None; |
| 2447 | } else { |
| 2448 | element = input_tangents[i]; |
| 2449 | } |
| 2450 | Py_INCREF(element); |
| 2451 | PyTuple_SET_ITEM(py_input_tangents.get(), i, element); |
| 2452 | } |
| 2453 | // Normalize the input sequence to a tuple so it works with function |
| 2454 | // caching; otherwise it may be an opaque _InputList object. |
| 2455 | tensorflow::Safe_PyObjectPtr input_tuple(PySequence_Tuple(inputs)); |
| 2456 | tensorflow::Safe_PyObjectPtr callback_args( |
| 2457 | Py_BuildValue("OOOOO", op_name, attrs, input_tuple.get(), results, |
| 2458 | py_input_tangents.get())); |
| 2459 | tensorflow::Safe_PyObjectPtr py_result( |
| 2460 | PyObject_CallObject(forward_gradient_function, callback_args.get())); |
| 2461 | if (py_result == nullptr || PyErr_Occurred()) { |
| 2462 | return tensorflow::errors::Internal( |
| 2463 | "forward gradient function threw exceptions"); |
| 2464 | } |
| 2465 | if (py_result.get() == Py_None) { |
| 2466 | // No connected gradients. |
| 2467 | return tensorflow::Status::OK(); |
| 2468 | } |
| 2469 | tensorflow::Safe_PyObjectPtr fast_result(PySequence_Fast( |
| 2470 | py_result.get(), "expected a sequence of forward gradients")); |
| 2471 | if (fast_result == nullptr) { |
| 2472 | return tensorflow::errors::InvalidArgument( |
| 2473 | "forward gradient function did not return a sequence."); |
| 2474 | } |
| 2475 | int len = PySequence_Fast_GET_SIZE(fast_result.get()); |
| 2476 | output_tangents->reserve(len); |
| 2477 | for (int i = 0; i < len; ++i) { |
| 2478 | PyObject* item = PySequence_Fast_GET_ITEM(fast_result.get(), i); |
| 2479 | if (item == Py_None) { |
| 2480 | output_tangents->push_back(nullptr); |
| 2481 | } else { |
| 2482 | Py_INCREF(item); |
| 2483 | output_tangents->push_back(item); |
| 2484 | } |
| 2485 | } |
| 2486 | return tensorflow::Status::OK(); |
| 2487 | } |
| 2488 | |
| 2489 | PyObject* RecordGradient(PyObject* op_name, PyObject* inputs, PyObject* attrs, |
| 2490 | PyObject* results, PyObject* name) { |
no test coverage detected