From 9c7c7c9979d66e97aeb8717d87f351df5ecbf56b Mon Sep 17 00:00:00 2001
From: Gaurav Jain <gjn@google.com>
Date: Mon, 10 Feb 2020 10:52:45 -0800
Subject: [PATCH] Make experimental_ref no longer experimental

Also clean up related documentation to utilize doctest.

PiperOrigin-RevId: 294256062
Change-Id: I9d7ff8dc1324bda64f3638ba3f8e48a0fcb42545
---
 tensorflow/python/client/session.py           | 20 ++--
 tensorflow/python/eager/backprop.py           |  2 +-
 tensorflow/python/eager/function_test.py      | 11 +--
 tensorflow/python/eager/ops_test.py           |  4 +-
 tensorflow/python/framework/function.py       |  8 +-
 tensorflow/python/framework/ops.py            | 72 +++++++-------
 tensorflow/python/framework/ops_test.py       | 96 +++++++++----------
 tensorflow/python/keras/layers/core.py        | 12 ++-
 .../experimental/autocast_variable.py         |  2 +-
 .../python/keras/optimizer_v2/adam_test.py    |  3 +-
 tensorflow/python/ops/custom_gradient.py      | 23 ++---
 tensorflow/python/ops/math_grad.py            | 13 ++-
 tensorflow/python/ops/parallel_for/pfor.py    |  2 +-
 tensorflow/python/ops/variables.py            | 68 ++++++-------
 tensorflow/python/ops/while_v2.py             |  5 +-
 tensorflow/python/training/moving_averages.py | 16 ++--
 .../api/golden/v1/tensorflow.-tensor.pbtxt    |  4 +
 .../api/golden/v1/tensorflow.-variable.pbtxt  |  4 +
 .../api/golden/v2/tensorflow.-tensor.pbtxt    |  4 +
 .../api/golden/v2/tensorflow.-variable.pbtxt  |  4 +
 20 files changed, 187 insertions(+), 186 deletions(-)

diff --git a/tensorflow/python/client/session.py b/tensorflow/python/client/session.py
index 4290a278f18..65ecc205369 100644
--- a/tensorflow/python/client/session.py
+++ b/tensorflow/python/client/session.py
@@ -494,10 +494,8 @@ class _FetchHandler(object):
       if (isinstance(fetch, ops.Tensor) and
           (fetch.op.type == 'GetSessionHandle' or
            fetch.op.type == 'GetSessionHandleV2')):
-        self._fetch_handles[fetch.experimental_ref()] = fetch.op.inputs[0].dtype
-    self._final_fetches = [
-        x for x in self._fetches if x.experimental_ref() not in feeds
-    ]
+        self._fetch_handles[fetch.ref()] = fetch.op.inputs[0].dtype
+    self._final_fetches = [x for x in self._fetches if x.ref() not in feeds]
 
   def _assert_fetchable(self, graph, op):
     if not graph.is_fetchable(op):
@@ -553,16 +551,16 @@ class _FetchHandler(object):
       else:
         # If the fetch was in the feeds, use the fed value, otherwise
         # use the returned value.
-        if self._fetches[i].experimental_ref() in self._feed_handles:
+        if self._fetches[i].ref() in self._feed_handles:
           # A fetch had a corresponding direct TensorHandle feed. Call eval()
           # to obtain the Tensor value from the TensorHandle.
-          value = self._feed_handles[self._fetches[i].experimental_ref()].eval()
+          value = self._feed_handles[self._fetches[i].ref()].eval()
         else:
-          value = self._feeds.get(self._fetches[i].experimental_ref())
+          value = self._feeds.get(self._fetches[i].ref())
         if value is None:
           value = tensor_values[j]
           j += 1
-        dtype = self._fetch_handles.get(self._fetches[i].experimental_ref())
+        dtype = self._fetch_handles.get(self._fetches[i].ref())
         if dtype:
           full_values.append(session_ops.TensorHandle(value, dtype, session))
         else:
@@ -1147,7 +1145,7 @@ class BaseSession(SessionInterface):
                                              session_ops.TensorHandle)
           if is_tensor_handle_feed:
             np_val = subfeed_val.to_numpy_array()
-            feed_handles[subfeed_t.experimental_ref()] = subfeed_val
+            feed_handles[subfeed_t.ref()] = subfeed_val
           else:
             np_val = np.asarray(subfeed_val, dtype=subfeed_dtype)
 
@@ -1160,7 +1158,7 @@ class BaseSession(SessionInterface):
           if not self.graph.is_feedable(subfeed_t):
             raise ValueError('Tensor %s may not be fed.' % subfeed_t)
 
-          feed_dict_tensor[subfeed_t.experimental_ref()] = np_val
+          feed_dict_tensor[subfeed_t.ref()] = np_val
           feed_map[compat.as_bytes(subfeed_t.name)] = (subfeed_t, subfeed_val)
 
     # Create a fetch handler to take care of the structure of fetches.
@@ -1435,7 +1433,7 @@ class BaseSession(SessionInterface):
         np_val = np.array(handle.handle, dtype=np.object)
         feed_name = handle_mover[0]
         feed_tensor = feed_map[feed_name][0]
-        feed_dict[feed_tensor.experimental_ref()] = np_val
+        feed_dict[feed_tensor.ref()] = np_val
       return handles
 
   def _call_tf_sessionrun(self, options, feed_dict, fetch_list, target_list,
diff --git a/tensorflow/python/eager/backprop.py b/tensorflow/python/eager/backprop.py
index 183961d03ca..42d8130fec8 100644
--- a/tensorflow/python/eager/backprop.py
+++ b/tensorflow/python/eager/backprop.py
@@ -659,7 +659,7 @@ def _zeros(shape, dtype):
   device = ctx.device_name
 
   if tensor_util.is_tensor(shape):
-    shape_key = shape.experimental_ref()
+    shape_key = shape.ref()
   else:
     shape_key = shape
   cache_key = shape_key, dtype, device
diff --git a/tensorflow/python/eager/function_test.py b/tensorflow/python/eager/function_test.py
index 29b463a5445..b03586633ee 100644
--- a/tensorflow/python/eager/function_test.py
+++ b/tensorflow/python/eager/function_test.py
@@ -361,11 +361,10 @@ class FunctionTest(test.TestCase, parameterized.TestCase):
     cf = f.get_concrete_function()
     c = cc[0]
 
-    captured_variables = {v.experimental_ref() for v in (a, b, c)}
-    trainable_variables = {v.experimental_ref() for v in (b, c)}
-    self.assertEqual({v.experimental_ref() for v in cf.variables},
-                     captured_variables)
-    self.assertEqual({v.experimental_ref() for v in cf.trainable_variables},
+    captured_variables = {v.ref() for v in (a, b, c)}
+    trainable_variables = {v.ref() for v in (b, c)}
+    self.assertEqual({v.ref() for v in cf.variables}, captured_variables)
+    self.assertEqual({v.ref() for v in cf.trainable_variables},
                      trainable_variables)
     self.assertEqual(cf.variables, cf.graph.variables)
     self.assertEqual(cf.trainable_variables, cf.graph.trainable_variables)
@@ -2889,7 +2888,7 @@ class FunctionTest(test.TestCase, parameterized.TestCase):
   def testDecoratedMethodVariableCleanup(self):
     m = DefunnedMiniModel()
     m(array_ops.ones([1, 2]))
-    variable_refs = list({v.experimental_ref() for v in m.variables})
+    variable_refs = list({v.ref() for v in m.variables})
     self.assertLen(variable_refs, 2)
     del m
 
diff --git a/tensorflow/python/eager/ops_test.py b/tensorflow/python/eager/ops_test.py
index 569b06fc0e3..0e9b6283237 100644
--- a/tensorflow/python/eager/ops_test.py
+++ b/tensorflow/python/eager/ops_test.py
@@ -426,8 +426,8 @@ class OpsTest(test_util.TensorFlowTestCase, parameterized.TestCase):
 
     strong_x = constant_op.constant([[1.]])
     strong_y = constant_op.constant([[2.]])
-    strong_x_ref = strong_x.experimental_ref()
-    strong_y_ref = strong_y.experimental_ref()
+    strong_x_ref = strong_x.ref()
+    strong_y_ref = strong_y.ref()
     weak_key_dict[strong_x_ref] = constant_op.constant([[3.]])
     weak_key_dict[strong_y_ref] = constant_op.constant([[4.]])
     strong_y.a = constant_op.constant([[5.]])
diff --git a/tensorflow/python/framework/function.py b/tensorflow/python/framework/function.py
index ff396475d0b..b44a4b85c6c 100644
--- a/tensorflow/python/framework/function.py
+++ b/tensorflow/python/framework/function.py
@@ -813,9 +813,9 @@ class _FuncGraph(ops.Graph):
 
   def capture(self, tensor, name=None):
     """Adds the given tensor to this graph and returns the captured tensor."""
-    if tensor.experimental_ref() in self._captured:
+    if tensor.ref() in self._captured:
       # Captured already.
-      return self._captured[tensor.experimental_ref()]
+      return self._captured[tensor.ref()]
     elif self._capture_by_value:
       return self._add_tensor_and_parents(tensor)
     else:
@@ -848,7 +848,7 @@ class _FuncGraph(ops.Graph):
                                   compat.as_bytes(handle_data))
     # pylint: enable=protected-access
     self.inputs.append(ph)
-    self._captured[tensor.experimental_ref()] = ph
+    self._captured[tensor.ref()] = ph
     self.extra_args.append(ph)
     if _is_guaranteed_const(tensor):
       with ops.control_dependencies(None):
@@ -881,7 +881,7 @@ class _FuncGraph(ops.Graph):
         op_def=op_def)
 
     for t, captured_t in zip(op.outputs, captured_op.outputs):
-      self._captured[t.experimental_ref()] = captured_t
+      self._captured[t.ref()] = captured_t
 
     return captured_op
 
diff --git a/tensorflow/python/framework/ops.py b/tensorflow/python/framework/ops.py
index a49966c9f85..79bc55e9e35 100644
--- a/tensorflow/python/framework/ops.py
+++ b/tensorflow/python/framework/ops.py
@@ -724,8 +724,8 @@ class Tensor(_TensorLike):
     g = getattr(self, "graph", None)
     if (Tensor._USE_EQUALITY and executing_eagerly_outside_functions() and
         (g is None or g.building_function)):
-      raise TypeError("Tensor is unhashable if Tensor equality is enabled. "
-                      "Instead, use tensor.experimental_ref() as the key.")
+      raise TypeError("Tensor is unhashable. "
+                      "Instead, use tensor.ref() as the key.")
     else:
       return id(self)
 
@@ -814,56 +814,50 @@ class Tensor(_TensorLike):
     """
     return _eval_using_default_session(self, feed_dict, self.graph, session)
 
+  @deprecation.deprecated(None, "Use ref() instead.")
   def experimental_ref(self):
-    # tf.Variable also has the same experimental_ref() API.  If you update the
-    # documenation here, please update tf.Variable.experimental_ref() as well.
+    return self.ref()
+
+  def ref(self):
+    # tf.Variable also has the same ref() API.  If you update the
+    # documentation here, please update tf.Variable.ref() as well.
     """Returns a hashable reference object to this Tensor.
 
     Warning: Experimental API that could be changed or removed.
 
-    The primary usecase for this API is to put tensors in a set/dictionary.
+    The primary use case for this API is to put tensors in a set/dictionary.
     We can't put tensors in a set/dictionary as `tensor.__hash__()` is no longer
     available starting Tensorflow 2.0.
 
-    ```python
-    import tensorflow as tf
+    The following will raise an exception starting 2.0
 
-    x = tf.constant(5)
-    y = tf.constant(10)
-    z = tf.constant(10)
+    >>> x = tf.constant(5)
+    >>> y = tf.constant(10)
+    >>> z = tf.constant(10)
+    >>> tensor_set = {x, y, z}
+    Traceback (most recent call last):
+      ...
+    TypeError: Tensor is unhashable. Instead, use tensor.ref() as the key.
+    >>> tensor_dict = {x: 'five', y: 'ten'}
+    Traceback (most recent call last):
+      ...
+    TypeError: Tensor is unhashable. Instead, use tensor.ref() as the key.
 
-    # The followings will raise an exception starting 2.0
-    # TypeError: Tensor is unhashable if Tensor equality is enabled.
-    tensor_set = {x, y, z}
-    tensor_dict = {x: 'five', y: 'ten', z: 'ten'}
-    ```
+    Instead, we can use `tensor.ref()`.
 
-    Instead, we can use `tensor.experimental_ref()`.
-
-    ```python
-    tensor_set = {x.experimental_ref(),
-                  y.experimental_ref(),
-                  z.experimental_ref()}
-
-    print(x.experimental_ref() in tensor_set)
-    ==> True
-
-    tensor_dict = {x.experimental_ref(): 'five',
-                   y.experimental_ref(): 'ten',
-                   z.experimental_ref(): 'ten'}
-
-    print(tensor_dict[y.experimental_ref()])
-    ==> ten
-    ```
+    >>> tensor_set = {x.ref(), y.ref(), z.ref()}
+    >>> x.ref() in tensor_set
+    True
+    >>> tensor_dict = {x.ref(): 'five', y.ref(): 'ten', z.ref(): 'ten'}
+    >>> tensor_dict[y.ref()]
+    'ten'
 
     Also, the reference object provides `.deref()` function that returns the
     original Tensor.
 
-    ```python
-    x = tf.constant(5)
-    print(x.experimental_ref().deref())
-    ==> tf.Tensor(5, shape=(), dtype=int32)
-    ```
+    >>> x = tf.constant(5)
+    >>> x.ref().deref()
+    <tf.Tensor: shape=(), dtype=int32, numpy=5>
     """
     return object_identity.Reference(self)
 
@@ -4425,12 +4419,12 @@ class Graph(object):
 
     def add_op(self, op):
       if isinstance(op, Tensor):
-        op = op.experimental_ref()
+        op = op.ref()
       self._seen_nodes.add(op)
 
     def op_in_group(self, op):
       if isinstance(op, Tensor):
-        op = op.experimental_ref()
+        op = op.ref()
       return op in self._seen_nodes
 
   def _push_control_dependencies_controller(self, controller):
diff --git a/tensorflow/python/framework/ops_test.py b/tensorflow/python/framework/ops_test.py
index dbd5abf7c5c..050285d3952 100644
--- a/tensorflow/python/framework/ops_test.py
+++ b/tensorflow/python/framework/ops_test.py
@@ -210,19 +210,19 @@ class TensorAndShapeTest(test_util.TensorFlowTestCase):
     z = constant_op.constant([6, 10])
     w = variables.Variable(5)
 
-    self.assertEqual(x1.experimental_ref(), x1.experimental_ref())
-    self.assertEqual(x2.experimental_ref(), x2.experimental_ref())
-    self.assertEqual(x1.experimental_ref(), x2.experimental_ref())
-    self.assertEqual(y.experimental_ref(), y.experimental_ref())
-    self.assertEqual(z.experimental_ref(), z.experimental_ref())
-    self.assertEqual(w.experimental_ref(), w.experimental_ref())
+    self.assertEqual(x1.ref(), x1.ref())
+    self.assertEqual(x2.ref(), x2.ref())
+    self.assertEqual(x1.ref(), x2.ref())
+    self.assertEqual(y.ref(), y.ref())
+    self.assertEqual(z.ref(), z.ref())
+    self.assertEqual(w.ref(), w.ref())
 
-    self.assertNotEqual(x1.experimental_ref(), y.experimental_ref())
-    self.assertNotEqual(x1.experimental_ref(), z.experimental_ref())
-    self.assertNotEqual(x1.experimental_ref(), w.experimental_ref())
-    self.assertNotEqual(y.experimental_ref(), z.experimental_ref())
-    self.assertNotEqual(y.experimental_ref(), w.experimental_ref())
-    self.assertNotEqual(z.experimental_ref(), w.experimental_ref())
+    self.assertNotEqual(x1.ref(), y.ref())
+    self.assertNotEqual(x1.ref(), z.ref())
+    self.assertNotEqual(x1.ref(), w.ref())
+    self.assertNotEqual(y.ref(), z.ref())
+    self.assertNotEqual(y.ref(), w.ref())
+    self.assertNotEqual(z.ref(), w.ref())
 
   def testRefDeref(self):
     x1 = constant_op.constant(3)
@@ -231,19 +231,19 @@ class TensorAndShapeTest(test_util.TensorFlowTestCase):
     z = constant_op.constant([6, 10])
     w = variables.Variable(5)
 
-    self.assertIs(x1, x1.experimental_ref().deref())
-    self.assertIs(x2, x2.experimental_ref().deref())
-    self.assertIs(x1, x2.experimental_ref().deref())
-    self.assertIs(x2, x1.experimental_ref().deref())
-    self.assertIs(y, y.experimental_ref().deref())
-    self.assertIs(z, z.experimental_ref().deref())
+    self.assertIs(x1, x1.ref().deref())
+    self.assertIs(x2, x2.ref().deref())
+    self.assertIs(x1, x2.ref().deref())
+    self.assertIs(x2, x1.ref().deref())
+    self.assertIs(y, y.ref().deref())
+    self.assertIs(z, z.ref().deref())
 
-    self.assertIsNot(x1, y.experimental_ref().deref())
-    self.assertIsNot(x1, z.experimental_ref().deref())
-    self.assertIsNot(x1, w.experimental_ref().deref())
-    self.assertIsNot(y, z.experimental_ref().deref())
-    self.assertIsNot(y, w.experimental_ref().deref())
-    self.assertIsNot(z, w.experimental_ref().deref())
+    self.assertIsNot(x1, y.ref().deref())
+    self.assertIsNot(x1, z.ref().deref())
+    self.assertIsNot(x1, w.ref().deref())
+    self.assertIsNot(y, z.ref().deref())
+    self.assertIsNot(y, w.ref().deref())
+    self.assertIsNot(z, w.ref().deref())
 
   def testRefInSet(self):
     x1 = constant_op.constant(3)
@@ -252,22 +252,22 @@ class TensorAndShapeTest(test_util.TensorFlowTestCase):
     z = constant_op.constant([6, 10])
     w = variables.Variable(5)
 
-    self.assertEqual(x1.experimental_ref(), x2.experimental_ref())
+    self.assertEqual(x1.ref(), x2.ref())
 
     tensor_set = {
-        x1.experimental_ref(),
-        x2.experimental_ref(),
-        y.experimental_ref(),
-        z.experimental_ref(),
-        w.experimental_ref(),
+        x1.ref(),
+        x2.ref(),
+        y.ref(),
+        z.ref(),
+        w.ref(),
     }
 
     self.assertEqual(len(tensor_set), 4)
-    self.assertIn(x1.experimental_ref(), tensor_set)
-    self.assertIn(x2.experimental_ref(), tensor_set)
-    self.assertIn(y.experimental_ref(), tensor_set)
-    self.assertIn(z.experimental_ref(), tensor_set)
-    self.assertIn(w.experimental_ref(), tensor_set)
+    self.assertIn(x1.ref(), tensor_set)
+    self.assertIn(x2.ref(), tensor_set)
+    self.assertIn(y.ref(), tensor_set)
+    self.assertIn(z.ref(), tensor_set)
+    self.assertIn(w.ref(), tensor_set)
 
   def testRefInDict(self):
     x1 = constant_op.constant(3)
@@ -276,36 +276,36 @@ class TensorAndShapeTest(test_util.TensorFlowTestCase):
     z = constant_op.constant([6, 10])
     w = variables.Variable(5)
 
-    self.assertEqual(x1.experimental_ref(), x2.experimental_ref())
+    self.assertEqual(x1.ref(), x2.ref())
 
     tensor_dict = {
-        x1.experimental_ref(): "x1",
-        y.experimental_ref(): "y",
-        z.experimental_ref(): "z",
-        w.experimental_ref(): "w",
+        x1.ref(): "x1",
+        y.ref(): "y",
+        z.ref(): "z",
+        w.ref(): "w",
     }
 
     self.assertEqual(len(tensor_dict), 4)
 
     # Overwriting x1
-    tensor_dict[x2.experimental_ref()] = "x2"
+    tensor_dict[x2.ref()] = "x2"
     self.assertEqual(len(tensor_dict), 4)
 
-    self.assertEqual(tensor_dict[x1.experimental_ref()], "x2")
-    self.assertEqual(tensor_dict[x2.experimental_ref()], "x2")
-    self.assertEqual(tensor_dict[y.experimental_ref()], "y")
-    self.assertEqual(tensor_dict[z.experimental_ref()], "z")
-    self.assertEqual(tensor_dict[w.experimental_ref()], "w")
+    self.assertEqual(tensor_dict[x1.ref()], "x2")
+    self.assertEqual(tensor_dict[x2.ref()], "x2")
+    self.assertEqual(tensor_dict[y.ref()], "y")
+    self.assertEqual(tensor_dict[z.ref()], "z")
+    self.assertEqual(tensor_dict[w.ref()], "w")
 
   def testTensorRefStrong(self):
     x = constant_op.constant(1.)
-    x_ref = x.experimental_ref()
+    x_ref = x.ref()
     del x
     self.assertIsNotNone(x_ref.deref())
 
   def testVariableRefStrong(self):
     x = variables.Variable(1.)
-    x_ref = x.experimental_ref()
+    x_ref = x.ref()
     del x
     self.assertIsNotNone(x_ref.deref())
 
diff --git a/tensorflow/python/keras/layers/core.py b/tensorflow/python/keras/layers/core.py
index c3ee6b1aaf3..8769a20b6b1 100644
--- a/tensorflow/python/keras/layers/core.py
+++ b/tensorflow/python/keras/layers/core.py
@@ -868,9 +868,10 @@ class Lambda(Layer):
       # checking only to immediately discard it.
       return
 
-    tracked_weights = set(v.experimental_ref() for v in self.weights)
-    untracked_new_vars = [v for v in created_variables
-                          if v.experimental_ref() not in tracked_weights]
+    tracked_weights = set(v.ref() for v in self.weights)
+    untracked_new_vars = [
+        v for v in created_variables if v.ref() not in tracked_weights
+    ]
     if untracked_new_vars:
       variable_str = '\n'.join('  {}'.format(i) for i in untracked_new_vars)
       error_str = textwrap.dedent(
@@ -886,8 +887,9 @@ class Lambda(Layer):
       ).format(name=self.name, variable_str=variable_str)
       raise ValueError(error_str)
 
-    untracked_used_vars = [v for v in accessed_variables
-                           if v.experimental_ref() not in tracked_weights]
+    untracked_used_vars = [
+        v for v in accessed_variables if v.ref() not in tracked_weights
+    ]
     if untracked_used_vars and not self._already_warned:
       variable_str = '\n'.join('  {}'.format(i) for i in untracked_used_vars)
       self._warn(textwrap.dedent(
diff --git a/tensorflow/python/keras/mixed_precision/experimental/autocast_variable.py b/tensorflow/python/keras/mixed_precision/experimental/autocast_variable.py
index 2248c93a3b1..589dcaf304f 100644
--- a/tensorflow/python/keras/mixed_precision/experimental/autocast_variable.py
+++ b/tensorflow/python/keras/mixed_precision/experimental/autocast_variable.py
@@ -149,7 +149,7 @@ class AutoCastVariable(variables.Variable):
   # reasons:
   #   * 'count_up_to': This method only applies to int variables, which cannot
   #     be wrapped with an AutoCastVariable.
-  #   * 'experimental_ref': Instead we inherit the definition from Variable.
+  #   * 'ref': Instead we inherit the definition from Variable.
   #     If we defined and delegated to Variable, the ref of an AutoCastVariable
   #     would be the same as the ref of the underlying variable, which would be
   #     strange as they are different Python objects.
diff --git a/tensorflow/python/keras/optimizer_v2/adam_test.py b/tensorflow/python/keras/optimizer_v2/adam_test.py
index 8259fc155a3..fd3df9cb4ba 100644
--- a/tensorflow/python/keras/optimizer_v2/adam_test.py
+++ b/tensorflow/python/keras/optimizer_v2/adam_test.py
@@ -539,8 +539,7 @@ class AdamOptimizerTest(test.TestCase):
       opt = adam.Adam(1.)
       opt.minimize(lambda: v1 + v2, var_list=[v1, v2])
       # There should be iteration, and two unique slot variables for v1 and v2.
-      self.assertEqual(5,
-                       len(set(v.experimental_ref() for v in opt.variables())))
+      self.assertEqual(5, len(set(v.ref() for v in opt.variables())))
       self.assertEqual(
           self.evaluate(opt.variables()[0]), self.evaluate(opt.iterations))
 
diff --git a/tensorflow/python/ops/custom_gradient.py b/tensorflow/python/ops/custom_gradient.py
index 6f46047c9a2..2592ba6ef97 100644
--- a/tensorflow/python/ops/custom_gradient.py
+++ b/tensorflow/python/ops/custom_gradient.py
@@ -311,14 +311,16 @@ def _graph_mode_decorator(f, args, kwargs):
   # Checking global and local variables attempts to ensure that no non-resource
   # Variables are added to the graph.
   current_var_scope = variable_scope.get_variable_scope()
-  before_vars = set(
-      [v.experimental_ref() for v in current_var_scope.global_variables() +
-       current_var_scope.local_variables()])
+  before_vars = set([
+      v.ref() for v in current_var_scope.global_variables() +
+      current_var_scope.local_variables()
+  ])
   with backprop.GradientTape() as tape:
     result, grad_fn = f(*args)
-  after_vars = set(
-      [v.experimental_ref() for v in current_var_scope.global_variables() +
-       current_var_scope.local_variables()])
+  after_vars = set([
+      v.ref() for v in current_var_scope.global_variables() +
+      current_var_scope.local_variables()
+  ])
   new_vars = after_vars - before_vars
   new_vars_list = [v.deref() for v in new_vars]
   for v in new_vars_list:
@@ -330,11 +332,10 @@ def _graph_mode_decorator(f, args, kwargs):
   # The variables that grad_fn needs to return gradients for are the set of
   # variables used that are *not* part of the inputs.
   inputs = args
-  variables_in_tape = frozenset([
-      v.experimental_ref() for v in tape.watched_variables()
-  ]) - frozenset(v.experimental_ref() for v in inputs)
+  variables_in_tape = frozenset([v.ref() for v in tape.watched_variables()
+                                ]) - frozenset(v.ref() for v in inputs)
   variables_in_subgraph = frozenset([
-      v.experimental_ref()
+      v.ref()
       for v in get_dependent_variables(input_ops=inputs, output_ops=result)
   ])
   variables = list(
@@ -411,7 +412,7 @@ def _eager_mode_decorator(f, args, kwargs):
   # variables used that are *not* part of the inputs.
   variables = [
       v.deref()  # pylint: disable=g-complex-comprehension
-      for v in set(v.experimental_ref() for v in tape.watched_variables())
+      for v in set(v.ref() for v in tape.watched_variables())
       if all(v.deref() is not i for i in all_inputs)
   ]
   grad_argspec = tf_inspect.getfullargspec(grad_fn)
diff --git a/tensorflow/python/ops/math_grad.py b/tensorflow/python/ops/math_grad.py
index 3191739ad02..614496e63e9 100644
--- a/tensorflow/python/ops/math_grad.py
+++ b/tensorflow/python/ops/math_grad.py
@@ -1705,19 +1705,18 @@ def _SparseMatMulGrad(op, grad):
   t_a = op.get_attr("transpose_a")
   t_b = op.get_attr("transpose_b")
   is_sparse = {}
-  is_sparse[op.inputs[0].experimental_ref()] = op.get_attr("a_is_sparse")
-  is_sparse[op.inputs[1].experimental_ref()] = op.get_attr("b_is_sparse")
+  is_sparse[op.inputs[0].ref()] = op.get_attr("a_is_sparse")
+  is_sparse[op.inputs[1].ref()] = op.get_attr("b_is_sparse")
   # Use heuristic to figure out if grad might be sparse
-  is_sparse[grad.experimental_ref()] = not context.executing_eagerly() and (
+  is_sparse[grad.ref()] = not context.executing_eagerly() and (
       grad.op.type == "ReluGrad")
 
   def _SparseMatMul(t1, t2, out_dtype, transpose_a=False, transpose_b=False):
     """Helper function to create SparseMatMul op."""
 
-    assert t1.experimental_ref() in is_sparse and t2.experimental_ref(
-    ) in is_sparse
-    t1_sparse = is_sparse[t1.experimental_ref()]
-    t2_sparse = is_sparse[t2.experimental_ref()]
+    assert t1.ref() in is_sparse and t2.ref() in is_sparse
+    t1_sparse = is_sparse[t1.ref()]
+    t2_sparse = is_sparse[t2.ref()]
     if transpose_b:
       t2 = array_ops.transpose(t2)
       transpose_b = False
diff --git a/tensorflow/python/ops/parallel_for/pfor.py b/tensorflow/python/ops/parallel_for/pfor.py
index 898593e3e45..c34ddb4f300 100644
--- a/tensorflow/python/ops/parallel_for/pfor.py
+++ b/tensorflow/python/ops/parallel_for/pfor.py
@@ -1622,7 +1622,7 @@ def _channel_flatten_input(x, data_format):
   """
 
   graph = ops.get_default_graph()
-  cache_key = (graph, x.experimental_ref(), data_format)
+  cache_key = (graph, x.ref(), data_format)
   if cache_key not in _channel_flatten_input_cache:
     x_shape = array_ops.shape(x)
     if data_format == b"NCHW":
diff --git a/tensorflow/python/ops/variables.py b/tensorflow/python/ops/variables.py
index 5e2fffaf1b7..f4fff36153a 100644
--- a/tensorflow/python/ops/variables.py
+++ b/tensorflow/python/ops/variables.py
@@ -1076,8 +1076,8 @@ class Variable(six.with_metaclass(VariableMetaclass, trackable.Trackable)):
 
   def __hash__(self):
     if ops.Tensor._USE_EQUALITY and ops.executing_eagerly_outside_functions():  # pylint: disable=protected-access
-      raise TypeError("Variable is unhashable if Tensor equality is enabled. "
-                      "Instead, use tensor.experimental_ref() as the key.")
+      raise TypeError("Variable is unhashable. "
+                      "Instead, use tensor.ref() as the key.")
     else:
       return id(self)
 
@@ -1209,56 +1209,50 @@ class Variable(six.with_metaclass(VariableMetaclass, trackable.Trackable)):
   def _get_save_slice_info(self):
     return self._save_slice_info
 
+  @deprecated(None, "Use ref() instead.")
   def experimental_ref(self):
-    # tf.Tensor also has the same experimental_ref() API.  If you update the
-    # documenation here, please update tf.Tensor.experimental_ref() as well.
+    return self.ref()
+
+  def ref(self):
+    # tf.Tensor also has the same ref() API.  If you update the
+    # documentation here, please update tf.Tensor.ref() as well.
     """Returns a hashable reference object to this Variable.
 
     Warning: Experimental API that could be changed or removed.
 
-    The primary usecase for this API is to put variables in a set/dictionary.
+    The primary use case for this API is to put variables in a set/dictionary.
     We can't put variables in a set/dictionary as `variable.__hash__()` is no
     longer available starting Tensorflow 2.0.
 
-    ```python
-    import tensorflow as tf
+    The following will raise an exception starting 2.0
 
-    x = tf.Variable(5)
-    y = tf.Variable(10)
-    z = tf.Variable(10)
+    >>> x = tf.Variable(5)
+    >>> y = tf.Variable(10)
+    >>> z = tf.Variable(10)
+    >>> variable_set = {x, y, z}
+    Traceback (most recent call last):
+      ...
+    TypeError: Variable is unhashable. Instead, use tensor.ref() as the key.
+    >>> variable_dict = {x: 'five', y: 'ten'}
+    Traceback (most recent call last):
+      ...
+    TypeError: Variable is unhashable. Instead, use tensor.ref() as the key.
 
-    # The followings will raise an exception starting 2.0
-    # TypeError: Variable is unhashable if Variable equality is enabled.
-    variable_set = {x, y, z}
-    variable_dict = {x: 'five', y: 'ten'}
-    ```
+    Instead, we can use `variable.ref()`.
 
-    Instead, we can use `variable.experimental_ref()`.
-
-    ```python
-    variable_set = {x.experimental_ref(),
-                    y.experimental_ref(),
-                    z.experimental_ref()}
-
-    print(x.experimental_ref() in variable_set)
-    ==> True
-
-    variable_dict = {x.experimental_ref(): 'five',
-                     y.experimental_ref(): 'ten',
-                     z.experimental_ref(): 'ten'}
-
-    print(variable_dict[y.experimental_ref()])
-    ==> ten
-    ```
+    >>> variable_set = {x.ref(), y.ref(), z.ref()}
+    >>> x.ref() in variable_set
+    True
+    >>> variable_dict = {x.ref(): 'five', y.ref(): 'ten', z.ref(): 'ten'}
+    >>> variable_dict[y.ref()]
+    'ten'
 
     Also, the reference object provides `.deref()` function that returns the
     original Variable.
 
-    ```python
-    x = tf.Variable(5)
-    print(x.experimental_ref().deref())
-    ==> <tf.Variable 'Variable:0' shape=() dtype=int32, numpy=5>
-    ```
+    >>> x = tf.Variable(5)
+    >>> x.ref().deref()
+    <tf.Variable 'Variable:0' shape=() dtype=int32, numpy=5>
     """
     return object_identity.Reference(self)
 
diff --git a/tensorflow/python/ops/while_v2.py b/tensorflow/python/ops/while_v2.py
index 4d4ae540a1c..d1ce8925839 100644
--- a/tensorflow/python/ops/while_v2.py
+++ b/tensorflow/python/ops/while_v2.py
@@ -460,8 +460,7 @@ def _get_intermediates(func_graph):
   # 3. Do not accumulate loop vars that are returned as-is just like captured
   #    tensors.
   intermediates = []
-  reverse_captures = dict(
-      (v.experimental_ref(), k) for k, v in func_graph.captures)
+  reverse_captures = dict((v.ref(), k) for k, v in func_graph.captures)
 
   for op in func_graph.get_operations():
     if op.type == "Identity":
@@ -473,7 +472,7 @@ def _get_intermediates(func_graph):
       if (o is not func_graph.inputs[0] and  # Loop counter.
           o.dtype != dtypes.resource and  # Do not accumulate resource tensors.
           _get_accumulator(o) is None and  # Has existing accumulator.
-          o.experimental_ref() not in reverse_captures
+          o.ref() not in reverse_captures
          ):  # Captured value, hence loop invariant.
         intermediates.append(o)
   return intermediates
diff --git a/tensorflow/python/training/moving_averages.py b/tensorflow/python/training/moving_averages.py
index 18b36d5b815..612215328c6 100644
--- a/tensorflow/python/training/moving_averages.py
+++ b/tensorflow/python/training/moving_averages.py
@@ -433,7 +433,7 @@ class ExponentialMovingAverage(object):
         raise TypeError("The variables must be half, float, or double: %s" %
                         var.name)
 
-      if var.experimental_ref() not in self._averages:
+      if var.ref() not in self._averages:
         # For variables: to lower communication bandwidth across devices we keep
         # the moving averages on the same device as the variables. For other
         # tensors, we rely on the existing device allocation mechanism.
@@ -455,8 +455,8 @@ class ExponentialMovingAverage(object):
                     "Variable", "VariableV2", "VarHandleOp"
                 ]))
             if self._zero_debias:
-              zero_debias_true.add(avg.experimental_ref())
-        self._averages[var.experimental_ref()] = avg
+              zero_debias_true.add(avg.ref())
+        self._averages[var.ref()] = avg
 
     with ops.name_scope(self.name) as scope:
       decay = ops.convert_to_tensor(self._decay, name="decay")
@@ -467,8 +467,8 @@ class ExponentialMovingAverage(object):
                                  (1.0 + num_updates) / (10.0 + num_updates))
       updates = []
       for var in var_list:
-        avg = self._averages[var.experimental_ref()]
-        zero_debias = avg.experimental_ref() in zero_debias_true
+        avg = self._averages[var.ref()]
+        zero_debias = avg.ref() in zero_debias_true
         updates.append(assign_moving_average(avg, var, decay, zero_debias))
       return control_flow_ops.group(*updates, name=scope)
 
@@ -482,7 +482,7 @@ class ExponentialMovingAverage(object):
       A `Variable` object or `None` if the moving average of `var`
       is not maintained.
     """
-    return self._averages.get(var.experimental_ref(), None)
+    return self._averages.get(var.ref(), None)
 
   def average_name(self, var):
     """Returns the name of the `Variable` holding the average for `var`.
@@ -506,8 +506,8 @@ class ExponentialMovingAverage(object):
       by the `ExponentialMovingAverage class` to hold the moving average of
       `var`.
     """
-    if var.experimental_ref() in self._averages:
-      return self._averages[var.experimental_ref()].op.name
+    if var.ref() in self._averages:
+      return self._averages[var.ref()].op.name
     return ops.get_default_graph().unique_name(
         var.op.name + "/" + self.name, mark_as_used=False)
 
diff --git a/tensorflow/tools/api/golden/v1/tensorflow.-tensor.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.-tensor.pbtxt
index 9f35e140d43..fd80af51d2a 100644
--- a/tensorflow/tools/api/golden/v1/tensorflow.-tensor.pbtxt
+++ b/tensorflow/tools/api/golden/v1/tensorflow.-tensor.pbtxt
@@ -55,6 +55,10 @@ tf_class {
     name: "get_shape"
     argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None"
   }
+  member_method {
+    name: "ref"
+    argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None"
+  }
   member_method {
     name: "set_shape"
     argspec: "args=[\'self\', \'shape\'], varargs=None, keywords=None, defaults=None"
diff --git a/tensorflow/tools/api/golden/v1/tensorflow.-variable.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.-variable.pbtxt
index df68721be37..37c7db00215 100644
--- a/tensorflow/tools/api/golden/v1/tensorflow.-variable.pbtxt
+++ b/tensorflow/tools/api/golden/v1/tensorflow.-variable.pbtxt
@@ -112,6 +112,10 @@ tf_class {
     name: "read_value"
     argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None"
   }
+  member_method {
+    name: "ref"
+    argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None"
+  }
   member_method {
     name: "scatter_add"
     argspec: "args=[\'self\', \'sparse_delta\', \'use_locking\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'None\'], "
diff --git a/tensorflow/tools/api/golden/v2/tensorflow.-tensor.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.-tensor.pbtxt
index 9f35e140d43..fd80af51d2a 100644
--- a/tensorflow/tools/api/golden/v2/tensorflow.-tensor.pbtxt
+++ b/tensorflow/tools/api/golden/v2/tensorflow.-tensor.pbtxt
@@ -55,6 +55,10 @@ tf_class {
     name: "get_shape"
     argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None"
   }
+  member_method {
+    name: "ref"
+    argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None"
+  }
   member_method {
     name: "set_shape"
     argspec: "args=[\'self\', \'shape\'], varargs=None, keywords=None, defaults=None"
diff --git a/tensorflow/tools/api/golden/v2/tensorflow.-variable.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.-variable.pbtxt
index f53a5ec6beb..c01fb767fae 100644
--- a/tensorflow/tools/api/golden/v2/tensorflow.-variable.pbtxt
+++ b/tensorflow/tools/api/golden/v2/tensorflow.-variable.pbtxt
@@ -111,6 +111,10 @@ tf_class {
     name: "read_value"
     argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None"
   }
+  member_method {
+    name: "ref"
+    argspec: "args=[\'self\'], varargs=None, keywords=None, defaults=None"
+  }
   member_method {
     name: "scatter_add"
     argspec: "args=[\'self\', \'sparse_delta\', \'use_locking\', \'name\'], varargs=None, keywords=None, defaults=[\'False\', \'None\'], "