code: def _compute_theoretical_jacobian(
      x, x_shape, x_data, dy, dy_shape, dx, extra_feed_dict
  ):
      """Computes the theoretical Jacobian for dy/dx.
  
      Computes the theoretical Jacobian using the ops generated by
      compute_gradient().
  
      Args:
        x: the tensor "x".
        x_shape: the dimensions of x as a tuple or an array of ints.
        x_data: a numpy parray as the input data for x
        dy: the tensor "dy".
        dy_shape: the dimensions of dy as a tuple or an array of ints.
        dx: Tensor or IndexedSlices representing dx
        extra_feed_dict: dict that allows fixing specified tensor values
          during the jacobian calculation.
  
      Returns:
        A 2-d numpy array representing the Jacobian for dy/dx. It has "x_size" rows
        and "dy_size" columns where "x_size" is the number of elements in x and
        "dy_size" is the number of elements in dy.
  
      Raises:
        ValueError: If `dy` is empty but the gradient is nonzero.
      """
      # Complex vectors are treated as vectors of twice as many reals.
      if x.dtype.is_complex:
          x_shape = tuple(x_shape) + (2,)
      dy_factor = 2 if dy.dtype.is_complex else 1
  
      # To compute the jacobian, we treat x and y as one-dimensional vectors.
      x_size = _product(x_shape)
      x_val_size = _product(
          x_shape[1:]
      )  # This is used for sparse gradients
      dy_size = _product(dy_shape) * dy_factor
  
      # Allocate 2-D Jacobian, with x dimensions smashed into the first
      # dimension and y dimensions smashed into the second.
      jacobian = np.zeros(
          (x_size, dy_size), dtype=x.dtype.real_dtype.as_numpy_dtype
      )
  
      # For each of the entry of dy, we set this to be 1 and
      # everything else to be 0 and compute the backprop -- this will give us one
      # one column of the Jacobian matrix.
      dy_data = np.zeros(dy_shape, dtype=dy.dtype.as_numpy_dtype)
      dy_data_flat = dy_data.ravel().view(
          dy.dtype.real_dtype.as_numpy_dtype
      )
      sess = ops.get_default_session()
      for col in range(dy_size):
          dy_data_flat[col] = 1
          if isinstance(dx, indexed_slices.IndexedSlices):
              backprop_indices, backprop_values = sess.run(
                  [dx.indices, dx.values],
                  feed_dict=_extra_feeds(
                      extra_feed_dict, {x: x_data, dy: dy_data}
                  ),
              )
              for i, v in zip(backprop_indices, backprop_values):
                  r_begin = i * x_val_size
                  r_end = r_begin + x_val_size
                  jacobian[r_begin:r_end, col] += v.flat
          else:
              assert isinstance(dx, tensor.Tensor), "dx = " + str(dx)
              backprop = sess.run(
                  dx,
                  feed_dict=_extra_feeds(
                      extra_feed_dict, {x: x_data, dy: dy_data}
                  ),
              )
              jacobian[:, col] = backprop.ravel().view(jacobian.dtype)
          dy_data_flat[col] = 0
  
      # If the output is empty, run the gradients at least once and make sure
      # they produce zeros.
      if not dy_size:
          backprop = sess.run(
              dx,
              feed_dict=_extra_feeds(
                  extra_feed_dict, {x: x_data, dy: dy_data}
              ),
          )
          if backprop.shape != x_data.shape:
              raise ValueError(
                  "Empty gradient has wrong shape: expected %s, got %s"
                  % (x_data.shape, backprop.shape)
              )
          if np.any(backprop):
              raise ValueError("Empty tensor with nonzero gradients")
  
      logging.vlog(1, "Theoretical Jacobian =\n%s", jacobian)
      return jacobian
  
imports: import numpy
  from tensorflow.python.framework import constant_op
  from tensorflow.python.framework import dtypes
  from tensorflow.python.framework import indexed_slices
  from tensorflow.python.framework import ops
  from tensorflow.python.framework import tensor
  from tensorflow.python.ops import array_ops
  from tensorflow.python.ops import gradients
  from tensorflow.python.ops import math_ops
  from tensorflow.python.platform import tf_logging
  from tensorflow.python.util import deprecation
  from tensorflow.python.util.tf_export import tf_export
label_desc: Use image node_id 3 for calling a global function with example usage: _compute_theoretical_jacobian(x, x_shape, x_data, dy, dy_shape, dx, extra_feed_dict) and returns: jacobian