code: def handle_adjoint_norm_(adjoint_options, shapes, state_norm):
      """In-place modifies the adjoint options to choose or wrap the norm function."""
  
      # This is the default adjoint norm on the backward pass: a mixed norm over the tuple of inputs.
      def default_adjoint_norm(tensor_tuple):
          t, y, adj_y, *adj_params = tensor_tuple
          # (If the state is actually a flattened tuple then this will be unpacked again in state_norm.)
          return max(
              t.abs(),
              state_norm(y),
              state_norm(adj_y),
              _mixed_norm(adj_params),
          )
  
      if "norm" not in adjoint_options:
          # `adjoint_options` was not explicitly specified by the user. Use the default norm.
          adjoint_options["norm"] = default_adjoint_norm
      else:
          # `adjoint_options` was explicitly specified by the user...
          try:
              adjoint_norm = adjoint_options["norm"]
          except KeyError:
              # ...but they did not specify the norm argument. Back to plan A: use the default norm.
              adjoint_options["norm"] = default_adjoint_norm
          else:
              # ...and they did specify the norm argument.
              if adjoint_norm == "seminorm":
                  # They told us they want to use seminorms. Slight modification to plan A: use the default norm,
                  # but ignore the parameter state
                  def adjoint_seminorm(tensor_tuple):
                      t, y, adj_y, *adj_params = tensor_tuple
                      # (If the state is actually a flattened tuple then this will be unpacked again in state_norm.)
                      return max(
                          t.abs(), state_norm(y), state_norm(adj_y)
                      )
  
                  adjoint_options["norm"] = adjoint_seminorm
              else:
                  # And they're using their own custom norm.
                  if shapes is None:
                      # The state on the forward pass was a tensor, not a tuple. We don't need to do anything, they're
                      # already going to get given the full adjoint state as (t, y, adj_y, adj_params)
                      pass  # this branch included for clarity
                  else:
                      # This is the bit that is tuple/tensor abstraction-breaking, because the odeint machinery
                      # doesn't know about the tupled nature of the forward state. We need to tell the user's adjoint
                      # norm about that ourselves.
  
                      def _adjoint_norm(tensor_tuple):
                          t, y, adj_y, *adj_params = tensor_tuple
                          y = _flat_to_shape(y, (), shapes)
                          adj_y = _flat_to_shape(adj_y, (), shapes)
                          return adjoint_norm(
                              (t, *y, *adj_y, *adj_params)
                          )
  
                      adjoint_options["norm"] = _adjoint_norm
  
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 3 for calling a global function with example usage: handle_adjoint_norm_(adjoint_options, shapes, state_norm) and returns: max, max, adjoint_norm