def autodiff(func,
             wrt=(0,),
             optimized=True,
             motion='joint',
             mode='reverse',
             preserve_result=False,
             check_dims=True,
             input_derivative=INPUT_DERIVATIVE.Required,
             verbose=0):
  """Build the vector-Jacobian or Jacobian-vector product of a function `func`.

  For a vector-Jacobian product (reverse-mode autodiff):
  This function proceeds by finding the primals and adjoints of all the
  functions in the call tree.
  For a Jacobian-vector product (forward-mode autodiff):
  We first find the primals and tangents of all functions in the call tree.

  It then wraps the top level function (i.e. the
  one passed as `func`) in a slightly more user-friendly interface. It then
  compiles the function and attaches to it the global namespace it needs to
  run.

  Args:
    func: The function to take the gradient of.
    wrt: A tuple of argument indices to differentiate with respect to. By
        default the derivative is taken with respect to the first argument.
    optimized: Whether to optimize the gradient function (`True` by default).
    motion: Either 'split' (separate functions for forward and backward pass)
        or 'joint' motion (a single combined function). Joint mode is the
        default.
    mode: Either 'forward' or 'reverse' mode. Forward mode is more efficient
        when the input dimensionality is lower than the output dimensionality,
        whereas it is the opposite for reverse mode.
    input_derivative: An enum indicating whether the user must supply an input
        derivative, and if not, what the default value is. See the
        possible values of INPUT_DERIVATIVE in this file.

    preserve_result: A boolean indicating whether or not the generated gradient
        function should also return the output of the original function.
        If False, the return signature of the input and output functions will be
        > val = func(*args)
        > df = grad(func,preserve_result=False)
        > gradval = df(*args)
        If True,
        > val = func(*args)
        > df = grad(func,preserve_result=True)
        > gradval, val = df(*args)
        Note that if taking gradients with respect to multiple arguments,
        the primal value will be appended to the return signature. Ex:
        > val = func(x,y)
        > df = grad(func,wrt=(0,1),preserve_result=True)
        > dx,dy,val = df(x,y)

    verbose: If 1 the source code of the generated functions will be
        output to stdout at various stages of the process for debugging
        purposes. If > 1, all intermediate code generation steps will print.

  Returns:
    df: A function that calculates a derivative (see file-level documentation
    above
        for the kinds of derivatives available) with respect to arguments
        specified in `wrt`, using forward or reverse mode according to `mode`.
        If using reverse mode, the gradient is calculated in either split
        or joint motion according to the value passed in `motion`. If
        `preserve_result` is True, the function will also return the original
        result of `func`.
  """
  # If the function had the with insert_grad_of statements removed, retrieve them
  func = getattr(func, 'tangent', func)

  # Generate the derivative
  node, namespace = autodiff_tree(func, wrt, motion, mode, preserve_result,
                                  check_dims, verbose)

  if mode == 'reverse' and motion == 'joint':
    # Pull the stack definition and initial gradient into the function body
    # TODO: Use first FunctionDef instead of first element
    node.body[0] = _create_joint(node.body[0], func, wrt, input_derivative)
    if verbose >= 2:
      print('INLINED')
      print(quoting.to_source(node))
  if mode == 'forward':
    node = _create_forward(node)
  if optimized:
    # Optimize the resulting functions
    node = optimization.optimize(node)
  node = comments.remove_repeated_comments(node)
  if verbose >= 1:
    print(quoting.to_source(node))

  # Compile and return
  module = compile_.compile_file(node, namespace)
  if mode == 'forward' or motion == 'joint':
    return getattr(module, node.body[0].name)
  else:
    # Compiling the top-level function in split mode makes no sense, but we use
    # it for testing; hence we don't care about the source being readable
    forward = getattr(module, node.body[0].name)
    backward = getattr(module, node.body[1].name)

    # Imported here to avoid circular imports
    import tangent

    def df(*args, **kwargs):
      _stack = tangent.Stack()
      init_grad = kwargs.pop('init_grad', 1.0)
      forward(_stack, *args, **kwargs)
      dx = backward(_stack, init_grad, *args, **kwargs)
      if len(dx) == 1:
        dx, = dx
      return dx

    return df
Build the vector-Jacobian or Jacobian-vector product of a function `func`.

  For a vector-Jacobian product (reverse-mode autodiff):
  This function proceeds by finding the primals and adjoints of all the
  functions in the call tree.
  For a Jacobian-vector product (forward-mode autodiff):
  We first find the primals and tangents of all functions in the call tree.

  It then wraps the top level function (i.e. the
  one passed as `func`) in a slightly more user-friendly interface. It then
  compiles the function and attaches to it the global namespace it needs to
  run.

  Args:
    func: The function to take the gradient of.
    wrt: A tuple of argument indices to differentiate with respect to. By
        default the derivative is taken with respect to the first argument.
    optimized: Whether to optimize the gradient function (`True` by default).
    motion: Either 'split' (separate functions for forward and backward pass)
        or 'joint' motion (a single combined function). Joint mode is the
        default.
    mode: Either 'forward' or 'reverse' mode. Forward mode is more efficient
        when the input dimensionality is lower than the output dimensionality,
        whereas it is the opposite for reverse mode.
    input_derivative: An enum indicating whether the user must supply an input
        derivative, and if not, what the default value is. See the
        possible values of INPUT_DERIVATIVE in this file.

    preserve_result: A boolean indicating whether or not the generated gradient
        function should also return the output of the original function.
        If False, the return signature of the input and output functions will be
        > val = func(*args)
        > df = grad(func,preserve_result=False)
        > gradval = df(*args)
        If True,
        > val = func(*args)
        > df = grad(func,preserve_result=True)
        > gradval, val = df(*args)
        Note that if taking gradients with respect to multiple arguments,
        the primal value will be appended to the return signature. Ex:
        > val = func(x,y)
        > df = grad(func,wrt=(0,1),preserve_result=True)
        > dx,dy,val = df(x,y)

    verbose: If 1 the source code of the generated functions will be
        output to stdout at various stages of the process for debugging
        purposes. If > 1, all intermediate code generation steps will print.

  Returns:
    df: A function that calculates a derivative (see file-level documentation
    above
        for the kinds of derivatives available) with respect to arguments
        specified in `wrt`, using forward or reverse mode according to `mode`.
        If using reverse mode, the gradient is calculated in either split
        or joint motion according to the value passed in `motion`. If
        `preserve_result` is True, the function will also return the original
        result of `func`.