code: def odeint_adjoint(
      func,
      y0,
      t,
      *,
      rtol=1e-7,
      atol=1e-9,
      method=None,
      options=None,
      event_fn=None,
      adjoint_rtol=None,
      adjoint_atol=None,
      adjoint_method=None,
      adjoint_options=None,
      adjoint_params=None,
  ):
      # We need this in order to access the variables inside this module,
      # since we have no other way of getting variables along the execution path.
      if adjoint_params is None and not isinstance(func, nn.Module):
          raise ValueError(
              "func must be an instance of nn.Module to specify the adjoint parameters; alternatively they "
              "can be specified explicitly via the `adjoint_params` argument. If there are no parameters "
              "then it is allowable to set `adjoint_params=()`."
          )
  
      # Must come before _check_inputs as we don't want to use normalised input (in particular any changes to options)
      if adjoint_rtol is None:
          adjoint_rtol = rtol
      if adjoint_atol is None:
          adjoint_atol = atol
      if adjoint_method is None:
          adjoint_method = method
  
      if (
          adjoint_method != method
          and options is not None
          and adjoint_options is None
      ):
          raise ValueError(
              "If `adjoint_method != method` then we cannot infer `adjoint_options` from `options`. So as "
              "`options` has been passed then `adjoint_options` must be passed as well."
          )
  
      if adjoint_options is None:
          adjoint_options = (
              {k: v for k, v in options.items() if k != "norm"}
              if options is not None
              else {}
          )
      else:
          # Avoid in-place modifying a user-specified dict.
          adjoint_options = adjoint_options.copy()
  
      if adjoint_params is None:
          adjoint_params = tuple(find_parameters(func))
      else:
          adjoint_params = tuple(
              adjoint_params
          )  # in case adjoint_params is a generator.
  
      # Filter params that don't require gradients.
      oldlen_ = len(adjoint_params)
      adjoint_params = tuple(
          p for p in adjoint_params if p.requires_grad
      )
      if len(adjoint_params) != oldlen_:
          # Some params were excluded.
          # Issue a warning if a user-specified norm is specified.
          if "norm" in adjoint_options and callable(
              adjoint_options["norm"]
          ):
              warnings.warn(
                  "An adjoint parameter was passed without requiring gradient. For efficiency this will be "
                  "excluded from the adjoint pass, and will not appear as a tensor in the adjoint norm."
              )
  
      # Convert to flattened state.
      (
          shapes,
          func,
          y0,
          t,
          rtol,
          atol,
          method,
          options,
          event_fn,
          decreasing_time,
      ) = _check_inputs(
          func, y0, t, rtol, atol, method, options, event_fn, SOLVERS
      )
  
      # Handle the adjoint norm function.
      state_norm = options["norm"]
      handle_adjoint_norm_(adjoint_options, shapes, state_norm)
  
      ans = OdeintAdjointMethod.apply(
          shapes,
          func,
          y0,
          t,
          rtol,
          atol,
          method,
          options,
          event_fn,
          adjoint_rtol,
          adjoint_atol,
          adjoint_method,
          adjoint_options,
          t.requires_grad,
          *adjoint_params,
      )
  
      if event_fn is None:
          solution = ans
      else:
          event_t, solution = ans
          event_t = event_t.to(t)
          if decreasing_time:
              event_t = -event_t
  
      if shapes is not None:
          solution = _flat_to_shape(solution, (len(t),), shapes)
  
      if event_fn is None:
          return solution
      else:
          return event_t, solution
  
file_path: torchdiffeq/torchdiffeq/_impl/adjoint.py
imports: import warnings
  import torch
  import torch.nn
  from .odeint import SOLVERS, odeint
  from .misc import _check_inputs, _flat_to_shape, _mixed_norm, _all_callback_names, _all_adjoint_callback_names
label_desc: Use image node_id 1 for calling a global function with example usage: odeint_adjoint(func, y0, t) and returns: solution, event_t,  solution