code: def adjoint_jacobian(tape: QuantumTape, state=None):
      """Implements the adjoint method outlined in
      `Jones and Gacon <https://arxiv.org/abs/2009.02823>`__ to differentiate an input tape.
  
      After a forward pass, the circuit is reversed by iteratively applying adjoint
      gates to scan backwards through the circuit.
  
      .. note::
  
          The adjoint differentiation method has the following restrictions:
  
          * Cannot differentiate with respect to observables.
  
          * Cannot differentiate with respect to state-prep operations.
  
          * Observable being measured must have a matrix.
  
      Args:
          tape (QuantumTape): circuit that the function takes the gradient of
          state (TensorLike): the final state of the circuit; if not provided,
              the final state will be computed by executing the tape
  
      Returns:
          array or tuple[array]: the derivative of the tape with respect to trainable parameters.
          Dimensions are ``(len(observables), len(trainable_params))``.
  
      """
      # Map wires if custom wire labels used
      tape = tape.map_to_standard_wires()
  
      if isinstance(tape.measurements[0], qml.measurements.StateMP):
          return _adjoint_jacobian_state(tape)
  
      ket = state if state is not None else get_final_state(tape)[0]
  
      n_obs = len(tape.observables)
      bras = np.empty(
          [n_obs] + [2] * len(tape.wires), dtype=np.complex128
      )
      for kk, obs in enumerate(tape.observables):
          bras[kk, ...] = 2 * apply_operation(obs, ket)
  
      jac = np.zeros(
          (len(tape.observables), len(tape.trainable_params))
      )
  
      param_number = (
          len(
              tape.get_parameters(
                  trainable_only=False, operations_only=True
              )
          )
          - 1
      )
      trainable_param_number = len(tape.trainable_params) - 1
      for op in reversed(tape.operations[tape.num_preps :]):
          if isinstance(op, qml.Snapshot):
              continue
          adj_op = qml.adjoint(op)
          ket = apply_operation(adj_op, ket)
  
          if op.num_params == 1:
              if param_number in tape.trainable_params:
                  d_op_matrix = operation_derivative(op)
                  ket_temp = apply_operation(
                      qml.QubitUnitary(d_op_matrix, wires=op.wires), ket
                  )
                  jac[:, trainable_param_number] = _dot_product_real(
                      bras, ket_temp, len(tape.wires)
                  )
  
                  trainable_param_number -= 1
              param_number -= 1
  
          for kk in range(n_obs):
              bras[kk, ...] = apply_operation(adj_op, bras[kk, ...])
  
      # Post-process the Jacobian matrix for the new return
      jac = np.squeeze(jac)
  
      if jac.ndim == 0:
          return np.array(jac)
  
      if jac.ndim == 1:
          return tuple(np.array(j) for j in jac)
  
      # must be 2-dimensional
      return tuple(tuple(np.array(j_) for j_ in j) for j in jac)
  
file_path: pennylane/pennylane/devices/qubit/adjoint_jacobian.py
imports: from numbers import Number
  from typing import Tuple
  import numpy
  import pennylane
  from pennylane.operation import operation_derivative
  from pennylane.tape import QuantumTape
  from .apply_operation import apply_operation
  from .simulate import get_final_state
  from .initialize_state import create_initial_state
label_desc: Use image node_id 3 for calling a global function with example usage: adjoint_jacobian(tape, state) and returns: tuple, _adjoint_jacobian_state, np, tuple