# Copyright 2020 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Experimental impl for GradientTape using unified APIs, for testing only."""

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

from tensorflow.python.framework.experimental import _tape
from tensorflow.python.framework.experimental import context_stack
from tensorflow.python.framework.experimental import gradient_registry
from tensorflow.python.util import nest


class GradientTape(object):
  """GradientTape using the unified API."""

  def __init__(self, persistent=False):
    self._c_tape = _tape.Tape(persistent)
    ctx = context_stack.get_default()
    self._tape_context = _tape.TapeContext(
        ctx, self._c_tape, gradient_registry.get_global_registry())
    self._ctx_manager = None

  def watch(self, t):
    self._c_tape.Watch(t)

  # TODO(srbs): Add support for unconnected_gradients.
  def gradient(self, targets, sources, output_gradients=None):
    ctx = context_stack.get_default()
    vspace = _tape.TapeVSpace(ctx)
    flat_targets = nest.flatten(targets)
    flat_sources = nest.flatten(sources)
    out_grads = self._c_tape.ComputeGradient(vspace, flat_targets, flat_sources,
                                             output_gradients or [])
    return nest.pack_sequence_as(sources, out_grads)

  def __enter__(self):
    """Enters a context inside which operations are recorded on this tape."""
    self._ctx_manager = context_stack.set_default(self._tape_context)
    self._ctx_manager.__enter__()
    return self

  def __exit__(self, typ, value, traceback):
    self._ctx_manager.__exit__(typ, value, traceback)
    self._ctx_manager = None
