code: def _check_inputs(
      func, y0, t, rtol, atol, method, options, event_fn, SOLVERS
  ):
      if event_fn is not None:
          if len(t) != 2:
              raise ValueError(
                  f"We require len(t) == 2 when in event handling mode, but got len(t)={len(t)}."
              )
  
          # Combine event functions if the output is multivariate.
          event_fn = combine_event_functions(event_fn, t[0], y0)
  
      # Keep reference to original func as passed in
      original_func = func
  
      # Normalise to tensor (non-tupled) input
      shapes = None
      is_tuple = not isinstance(y0, torch.Tensor)
      if is_tuple:
          assert isinstance(
              y0, tuple
          ), "y0 must be either a torch.Tensor or a tuple"
          shapes = [y0_.shape for y0_ in y0]
          rtol = _tuple_tol("rtol", rtol, shapes)
          atol = _tuple_tol("atol", atol, shapes)
          y0 = torch.cat([y0_.reshape(-1) for y0_ in y0])
          func = _TupleFunc(func, shapes)
          if event_fn is not None:
              event_fn = _TupleInputOnlyFunc(event_fn, shapes)
  
      # Normalise method and options
      if options is None:
          options = {}
      else:
          options = options.copy()
      if method is None:
          method = "dopri5"
      if method not in SOLVERS:
          raise ValueError(
              'Invalid method "{}". Must be one of {}'.format(
                  method, '{"' + '", "'.join(SOLVERS.keys()) + '"}.'
              )
          )
  
      if is_tuple:
          # We accept tupled input. This is an abstraction that is hidden from the rest of odeint (exception when
          # returning values), so here we need to maintain the abstraction by wrapping norm functions.
  
          if "norm" in options:
              # If the user passed a norm then get that...
              norm = options["norm"]
          else:
              # ...otherwise we default to a mixed Linf/L2 norm over tupled input.
              norm = _mixed_norm
  
          # In either case, norm(...) is assumed to take a tuple of tensors as input. (As that's what the state looks
          # like from the point of view of the user.)
          # So here we take the tensor that the machinery of odeint has given us, and turn it in the tuple that the
          # norm function is expecting.
          def _norm(tensor):
              y = _flat_to_shape(tensor, (), shapes)
              return norm(y)
  
          options["norm"] = _norm
  
      else:
          if "norm" in options:
              # No need to change the norm function.
              pass
          else:
              # Else just use the default norm.
              # Technically we don't need to set that here (RKAdaptiveStepsizeODESolver has it as a default), but it
              # makes it easier to reason about, in the adjoint norm logic, if we know that options['norm'] is
              # definitely set to something.
              options["norm"] = _rms_norm
  
      # Normalise time
      _check_timelike("t", t, True)
      t_is_reversed = False
      if len(t) > 1 and t[0] > t[1]:
          t_is_reversed = True
  
      if t_is_reversed:
          # Change the integration times to ascending order.
          # We do this by negating the time values and all associated arguments.
          t = -t
  
          # Ensure time values are un-negated when calling functions.
          func = _ReverseFunc(func, mul=-1.0)
          if event_fn is not None:
              event_fn = _ReverseFunc(event_fn)
  
          # For fixed step solvers.
          try:
              _grid_constructor = options["grid_constructor"]
          except KeyError:
              pass
          else:
              options[
                  "grid_constructor"
              ] = lambda func, y0, t: -_grid_constructor(func, y0, -t)
  
          # For RK solvers.
          _flip_option(options, "step_t")
          _flip_option(options, "jump_t")
  
      # Can only do after having normalised time
      _assert_increasing("t", t)
  
      # Tol checking
      if torch.is_tensor(rtol):
          assert not rtol.requires_grad, "rtol cannot require gradient"
      if torch.is_tensor(atol):
          assert not atol.requires_grad, "atol cannot require gradient"
  
      # Backward compatibility: Allow t and y0 to be on different devices
      if t.device != y0.device:
          warnings.warn(
              "t is not on the same device as y0. Coercing to y0.device."
          )
          t = t.to(y0.device)
      # ~Backward compatibility
  
      # Add perturb argument to func.
      func = _PerturbFunc(func)
  
      # Add callbacks to wrapped_func
      callback_names = set()
      for callback_name in _all_callback_names:
          try:
              callback = getattr(original_func, callback_name)
          except AttributeError:
              setattr(func, callback_name, _null_callback)
          else:
              if callback is not _null_callback:
                  callback_names.add(callback_name)
                  # At the moment all callbacks have the arguments (t0, y0, dt).
                  # These will need adjusting on a per-callback basis if that changes in the future.
                  if is_tuple:
  
                      def callback(t0, y0, dt, _callback=callback):
                          y0 = _flat_to_shape(y0, (), shapes)
                          return _callback(t0, y0, dt)
  
                  if t_is_reversed:
  
                      def callback(t0, y0, dt, _callback=callback):
                          return _callback(-t0, y0, dt)
  
              setattr(func, callback_name, callback)
      for callback_name in _all_adjoint_callback_names:
          try:
              callback = getattr(original_func, callback_name)
          except AttributeError:
              pass
          else:
              setattr(func, callback_name, callback)
  
      invalid_callbacks = (
          callback_names - SOLVERS[method].valid_callbacks()
      )
      if len(invalid_callbacks) > 0:
          warnings.warn(
              "Solver '{}' does not support callbacks {}".format(
                  method, invalid_callbacks
              )
          )
  
      return (
          shapes,
          func,
          y0,
          t,
          rtol,
          atol,
          method,
          options,
          event_fn,
          t_is_reversed,
      )
  
imports: from enum import Enum
  import math
  import numpy
  import torch
  import warnings
  from .event_handling import combine_event_functions
label_desc: Use image node_id 15 for calling a global function with example usage: _check_inputs(func, y0, t, rtol, atol, method, options, event_fn, SOLVERS) and returns: shapes,  func,  y0,  t,  rtol,  atol,  method,  options,  event_fn,  t_is_reversed, norm, _callback, _callback