code: def _expval_pulse_odegen(tape, argnum, atol):
      """Compute the pulse generator parameter-shift rule for a quantum circuit that returns expectation
      values of observables.
  
      Args:
          tape (`~.QuantumTape`): Quantum circuit to be differentiated with the pulse generator
              parameter-shift rule.
          argnum (int or list[int] or None): Trainable tape parameter indices to differentiate
              with respect to. If not provided, the derivatives with respect to all
              trainable parameters are returned. Note that the indices are with respect to
              the list of trainable parameters.
          atol (float): absolute tolerance used to determine vanishing contributions.
  
      Returns:
          tuple[list[QuantumTape], function]: A tuple containing a
            list of generated tapes, together with a post-processing
            function to be applied to the results of the evaluated tapes
            in order to obtain the Jacobian.
  
      """
      argnum = argnum or tape.trainable_params
      # Initialize a cache that will store the following:
      # 1. a single entry ``str: int`` that memorizes the number of tapes that
      #    were created overall thus far. It is initialized in the following line
      # 2. one entry per ``ParametrizedEvolution`` that contains at least one trainable
      #    parameter of the tape that also is marked as trainable as per ``argnum``.
      #    These entries have the format ``int: (int, int, tuple[tensor_like])``. The key is the
      #    index for the pulse within the tape operations. The first two entries of the value
      #    are a "start" and an "end" pointer, referencing which tapes in ``gradient_tapes``
      #    have been created for the pulse.
      #    Correspondingly, these pointers define the sub-list of results that should be used
      #    for the given pulse in the postprocessing function below.
      #    The third entry in the value tuple contains the coefficients of the effective
      #    generators of the given pulse. These are given in a tuple, with one entry per
      #    parameter of the pulse.
      #    For details on these coefficients, see _one_parameter_paulirot_coeffs.
      cache = {"total_num_tapes": 0}
      # ``gradient_data`` will contain tuples ``(int, int, tensor_like)``, with the first two
      # entries being the start/end pointers explained above, and the third entry being the
      # coefficients tensor for one particular parameter of a pulse (one entry of the coefficients
      # tuple in the corresponding ``cache`` entry).
      gradient_data = []
      gradient_tapes = []
      tape_params = tape.get_parameters()
      for idx, param in enumerate(tape_params):
          shape = qml.math.shape(param)
  
          if idx not in argnum:
              # Trainable parameters that are de-selected by ``argnum`` receive a vanishing
              # gradient entry.
              # Indicate that there are no tapes for this parameter by setting start==end
              gradient_data.append((0, 0, None, shape))
              continue
  
          # Access the pulse in the tape into which the current trainable parameter
          # feeds. If this pulse is analyzed for the first time, compute its effective
          # generators and their Pauli basis coefficients for _all_ parameters of the pulse,
          # and create the tapes of the modified cost function for all occurring Pauli words.
          # If the pulse has been analyzed before, retrieve the tape/results pointers of
          # the pulse and the coefficients belonging to the current parameter from the cache,
          # but do not add create any additional tapes.
          tapes, data, cache = _generate_tapes_and_coeffs(
              tape, idx, atol, cache
          )
  
          gradient_data.append((*data, shape))
          gradient_tapes.extend(tapes)
  
      # Extract some specifications about the original tape, required for output formatting
      num_measurements = len(tape.measurements)
      single_measure = num_measurements == 1
      num_params = len(tape.trainable_params)
      partitioned_shots = tape.shots.has_partitioned_shots
      tape_specs = (
          single_measure,
          num_params,
          num_measurements,
          tape.shots,
      )
  
      def processing_fn(results):
          """Post-process the results of the parameter-shifted tapes for
          ``pulse_odegen`` into the gradient."""
          grads = []
          zero_parshapes = []
          # Iterate over gradient_data, which contains one entry for each of the trainable parameters in argnum
          for start, end, coeffs, par_shape in gradient_data:
              # If start and end pointer are equal, no tapes contribute and we get a vanishing
              # gradient for this parameter. For this, add an entry ``None``, which will
              # be replaced by the appropriately formatted zero later on. Memorize the parameter
              # shape for said formatting.
              if start == end:
                  grads.append(None)
                  zero_parshapes.append(par_shape)
                  continue
  
              # Extract the results corresponding to the start and end pointer of the pulse
              # to which the current parameter belongs.
              res = results[start:end]
              # Apply the parameter-shift rule (respecting the tape output formatting)
              # and contract the result with the coefficients of the effective generators
              # in the Pauli basis. This computes the partial derivative.
              g = _parshift_and_contract(
                  res, coeffs, single_measure, not partitioned_shots
              )
              grads.append(g)
  
              # Memorize the parameter shape for the nonzero gradient entry
              nonzero_parshape = par_shape
  
          # g will have been defined at least once (because otherwise all gradients would have
          # been zero), providing a representative for a zero gradient to emulate its type/shape.
          # This shape needs to be modified because pulse parameters may differ in shape.
          zero_parshapes = iter(zero_parshapes)
          for i, _g in enumerate(grads):
              if _g is None:
                  par_shapes = (nonzero_parshape, next(zero_parshapes))
                  # Make zero representative gradient entry, adapting the shape
                  zero_rep = _make_zero_rep(
                      g, single_measure, partitioned_shots, par_shapes
                  )
                  # Fill in zero-valued gradient entry
                  grads[i] = zero_rep
  
          # Reformat the flat list of gradients into an output that fits the original tape specs.
          return reorder_grads(grads, tape_specs)
  
      return gradient_tapes, processing_fn
  
file_path: pennylane/pennylane/gradients/pulse_gradient_odegen.py
imports: from typing import Callable, Sequence
  from functools import partial
  import numpy
  import pennylane
  from pennylane.pulse import ParametrizedEvolution
  from pennylane.ops.qubit.special_unitary import pauli_basis_strings, _pauli_decompose
  from pennylane import transform
  from .parameter_shift import _make_zero_rep
  from .pulse_gradient import _assert_has_jax, raise_pulse_diff_on_qnode
  from .gradient_transform import _all_zero_grad, assert_no_state_returns, assert_no_tape_batching, assert_no_variance, choose_trainable_params, find_and_validate_gradient_methods, _no_trainable_grad, reorder_grads
label_desc: Use image node_id 7 for calling a global function with example usage: _expval_pulse_odegen(tape, argnum, atol) and returns: gradient_tapes,  processing_fn, reorder_grads