class_docstr: Abstract base class for PennyLane qubit devices.
  
  The following abstract method **must** be defined:
  
  * :meth:`~.apply`: append circuit operations, compile the circuit (if applicable),
    and perform the quantum computation.
  
  Devices that generate their own samples (such as hardware) may optionally
  overwrite :meth:`~.probability`. This method otherwise automatically
  computes the probabilities from the generated samples, and **must**
  overwrite the following method:
  
  * :meth:`~.generate_samples`: Generate samples from the device from the
    exact or approximate probability distribution.
  
  Analytic devices **must** overwrite the following method:
  
  * :meth:`~.analytic_probability`: returns the probability or marginal probability from the
    device after circuit execution. :meth:`~.marginal_prob` may be used here.
  
  This device contains common utility methods for qubit-based devices. These
  do not need to be overwritten. Utility methods include:
  
  * :meth:`~.expval`, :meth:`~.var`, :meth:`~.sample`: return expectation values,
    variances, and samples of observables after the circuit has been rotated
    into the observable eigenbasis.
  
  Args:
      wires (int, Iterable[Number, str]]): Number of subsystems represented by the device,
          or iterable that contains unique labels for the subsystems as numbers (i.e., ``[-1, 0, 2]``)
          or strings (``['ancilla', 'q1', 'q2']``). Default 1 if not specified.
      shots (None, int, list[int]): Number of circuit evaluations/random samples used to estimate
          expectation values of observables. If ``None``, the device calculates probability, expectation values,
          and variances analytically. If an integer, it specifies the number of samples to estimate these quantities.
          If a list of integers is passed, the circuit evaluations are batched over the list of shots.
      r_dtype: Real floating point precision type.
      c_dtype: Complex floating point precision type.
docstr: Process measurement results from circuit execution and return statistics.
  
  This includes returning expectation values, variance, samples, probabilities, states, and
  density matrices.
  
  Args:
      circuit (~.tape.QuantumTape): the quantum tape currently being executed
      shot_range (tuple[int]): 2-tuple of integers specifying the range of samples
          to use. If not specified, all samples are used.
      bin_size (int): Divides the shot range into bins of size ``bin_size``, and
          returns the measurement statistic separately over each bin. If not
          provided, the entire shot range is treated as a single bin.
  
  Raises:
      QuantumFunctionError: if the value of :attr:`~.Observable.return_type` is not supported
  
  Returns:
      Union[float, List[float]]: the corresponding statistics
  
  .. details::
      :title: Usage Details
  
      The ``shot_range`` and ``bin_size`` arguments allow for the statistics
      to be performed on only a subset of device samples. This finer level
      of control is accessible from the main UI by instantiating a device
      with a batch of shots.
  
      For example, consider the following device:
  
      >>> dev = qml.device("my_device", shots=[5, (10, 3), 100])
  
      This device will execute QNodes using 135 shots, however
      measurement statistics will be **course grained** across these 135
      shots:
  
      * All measurement statistics will first be computed using the
        first 5 shots --- that is, ``shots_range=[0, 5]``, ``bin_size=5``.
  
      * Next, the tuple ``(10, 3)`` indicates 10 shots, repeated 3 times. We will want to use
        ``shot_range=[5, 35]``, performing the expectation value in bins of size 10
        (``bin_size=10``).
  
      * Finally, we repeat the measurement statistics for the final 100 shots,
        ``shot_range=[35, 135]``, ``bin_size=100``.
docstr_tok: ["Process","measurement","results","from","circuit","execution","and","return","statistics",".","This","includes","returning","expectation","values",",","variance",",","samples",",","probabilities",",","states",",","and","density","matrices",".","Args",":","circuit","(","~.tape.QuantumTape",")",":","the","quantum","tape","currently","being","executed","shot_range","(","tuple","[","int","]",")",":","2-tuple","of","integers","specifying","the","range","of","samples","to","use",".","If","not","specified",",","all","samples","are","used",".","bin_size","(","int",")",":","Divides","the","shot","range","into","bins","of","size","``","bin_size","``",",","and","returns","the","measurement","statistic","separately","over","each","bin",".","If","not","provided",",","the","entire","shot","range","is","treated","as","a","single","bin",".","Raises",":","QuantumFunctionError",":","if","the","value","of",":","attr",":","`","~.Observable.return_type","`","is","not","supported","Returns",":","Union","[","float",",","List","[","float","]","]",":","the","corresponding","statistics","..","details",":",":",":","title",":","Usage","Details","The","``","shot_range","``","and","``","bin_size","``","arguments","allow","for","the","statistics","to","be","performed","on","only","a","subset","of","device","samples",".","This","finer","level","of","control","is","accessible","from","the","main","UI","by","instantiating","a","device","with","a","batch","of","shots",".","For","example",",","consider","the","following","device",":",">",">",">","dev","=","qml.device","(","``","my_device","''",",","shots=","[","5",",","(","10",",","3",")",",","100","]",")","This","device","will","execute","QNodes","using","135","shots",",","however","measurement","statistics","will","be","*","*","course","grained","*","*","across","these","135","shots",":","*","All","measurement","statistics","will","first","be","computed","using","the","first","5","shots","--","-","that","is",",","``","shots_range=","[","0",",","5","]","``",",","``","bin_size=5","``",".","*","Next",",","the","tuple","``","(","10",",","3",")","``","indicates","10","shots",",","repeated","3","times",".","We","will","want","to","use","``","shot_range=","[","5",",","35","]","``",",","performing","the","expectation","value","in","bins","of","size","10","(","``","bin_size=10","``",")",".","*","Finally",",","we","repeat","the","measurement","statistics","for","the","final","100","shots",",","``","shot_range=","[","35",",","135","]","``",",","``","bin_size=100","``","."]
code: def statistics(
      self, circuit: QuantumTape, shot_range=None, bin_size=None
  ):  # pylint: disable=too-many-statements
      """Process measurement results from circuit execution and return statistics.
  
      This includes returning expectation values, variance, samples, probabilities, states, and
      density matrices.
  
      Args:
          circuit (~.tape.QuantumTape): the quantum tape currently being executed
          shot_range (tuple[int]): 2-tuple of integers specifying the range of samples
              to use. If not specified, all samples are used.
          bin_size (int): Divides the shot range into bins of size ``bin_size``, and
              returns the measurement statistic separately over each bin. If not
              provided, the entire shot range is treated as a single bin.
  
      Raises:
          QuantumFunctionError: if the value of :attr:`~.Observable.return_type` is not supported
  
      Returns:
          Union[float, List[float]]: the corresponding statistics
  
      .. details::
          :title: Usage Details
  
          The ``shot_range`` and ``bin_size`` arguments allow for the statistics
          to be performed on only a subset of device samples. This finer level
          of control is accessible from the main UI by instantiating a device
          with a batch of shots.
  
          For example, consider the following device:
  
          >>> dev = qml.device("my_device", shots=[5, (10, 3), 100])
  
          This device will execute QNodes using 135 shots, however
          measurement statistics will be **course grained** across these 135
          shots:
  
          * All measurement statistics will first be computed using the
            first 5 shots --- that is, ``shots_range=[0, 5]``, ``bin_size=5``.
  
          * Next, the tuple ``(10, 3)`` indicates 10 shots, repeated 3 times. We will want to use
            ``shot_range=[5, 35]``, performing the expectation value in bins of size 10
            (``bin_size=10``).
  
          * Finally, we repeat the measurement statistics for the final 100 shots,
            ``shot_range=[35, 135]``, ``bin_size=100``.
      """
      measurements = circuit.measurements
      results = []
  
      for m in measurements:
          # TODO: Remove this when all overriden measurements support the `MeasurementProcess` class
          if isinstance(m.mv, list):
              # MeasurementProcess stores information needed for processing if terminal measurement
              # uses a list of mid-circuit measurement values
              obs = m
          else:
              obs = m.obs or m.mv or m
          # Check if there is an overriden version of the measurement process
          if method := getattr(
              self, self.measurement_map[type(m)], False
          ):
              if isinstance(m, MeasurementTransform):
                  result = method(tape=circuit)
              else:
                  result = method(
                      m, shot_range=shot_range, bin_size=bin_size
                  )
          # 1. Based on the measurement type, compute statistics
          # Pass instances directly
          elif isinstance(m, ExpectationMP):
              result = self.expval(
                  obs, shot_range=shot_range, bin_size=bin_size
              )
  
          elif isinstance(m, VarianceMP):
              result = self.var(
                  obs, shot_range=shot_range, bin_size=bin_size
              )
  
          elif isinstance(m, SampleMP):
              samples = self.sample(
                  obs,
                  shot_range=shot_range,
                  bin_size=bin_size,
                  counts=False,
              )
              result = self._asarray(qml.math.squeeze(samples))
  
          elif isinstance(m, CountsMP):
              result = self.sample(
                  m,
                  shot_range=shot_range,
                  bin_size=bin_size,
                  counts=True,
              )
  
          elif isinstance(m, ProbabilityMP):
              result = self.probability(
                  wires=m.wires,
                  shot_range=shot_range,
                  bin_size=bin_size,
              )
  
          elif isinstance(m, StateMP):
              if len(measurements) > 1:
                  raise qml.QuantumFunctionError(
                      "The state or density matrix cannot be returned in combination "
                      "with other return types"
                  )
  
              if self.shots is not None:
                  warnings.warn(
                      "Requested state or density matrix with finite shots; the returned "
                      "state information is analytic and is unaffected by sampling. To silence "
                      "this warning, set shots=None on the device.",
                      UserWarning,
                  )
  
              # Check if the state is accessible and decide to return the state or the density
              # matrix.
              state = self.access_state(wires=obs.wires)
              result = self._asarray(state, dtype=self.C_DTYPE)
  
          elif isinstance(m, VnEntropyMP):
              if self.wires.labels != tuple(range(self.num_wires)):
                  raise qml.QuantumFunctionError(
                      "Returning the Von Neumann entropy is not supported when using custom wire labels"
                  )
  
              if self._shot_vector is not None:
                  raise NotImplementedError(
                      "Returning the Von Neumann entropy is not supported with shot vectors."
                  )
  
              if self.shots is not None:
                  warnings.warn(
                      "Requested Von Neumann entropy with finite shots; the returned "
                      "result is analytic and is unaffected by sampling. To silence "
                      "this warning, set shots=None on the device.",
                      UserWarning,
                  )
              result = self.vn_entropy(
                  wires=obs.wires, log_base=obs.log_base
              )
  
          elif isinstance(m, MutualInfoMP):
              if self.wires.labels != tuple(range(self.num_wires)):
                  raise qml.QuantumFunctionError(
                      "Returning the mutual information is not supported when using custom wire labels"
                  )
  
              if self._shot_vector is not None:
                  raise NotImplementedError(
                      "Returning the mutual information is not supported with shot vectors."
                  )
  
              if self.shots is not None:
                  warnings.warn(
                      "Requested mutual information with finite shots; the returned "
                      "state information is analytic and is unaffected by sampling. To silence "
                      "this warning, set shots=None on the device.",
                      UserWarning,
                  )
              wires0, wires1 = obs.raw_wires
              result = self.mutual_info(
                  wires0=wires0, wires1=wires1, log_base=obs.log_base
              )
  
          elif isinstance(m, ClassicalShadowMP):
              if len(measurements) > 1:
                  raise qml.QuantumFunctionError(
                      "Classical shadows cannot be returned in combination "
                      "with other return types"
                  )
              result = self.classical_shadow(obs, circuit)
  
          elif isinstance(m, ShadowExpvalMP):
              if len(measurements) > 1:
                  raise qml.QuantumFunctionError(
                      "Classical shadows cannot be returned in combination "
                      "with other return types"
                  )
              result = self.shadow_expval(obs, circuit=circuit)
  
          elif isinstance(m, MeasurementTransform):
              result = m.process(tape=circuit, device=self)
  
          elif isinstance(m, (SampleMeasurement, StateMeasurement)):
              result = self._measure(
                  m, shot_range=shot_range, bin_size=bin_size
              )
  
          elif m.return_type is not None:
              raise qml.QuantumFunctionError(
                  f"Unsupported return type specified for observable {obs.name}"
              )
          else:
              result = None
  
          # 2. Post-process statistics results (if need be)
          if isinstance(
              m,
              (
                  ExpectationMP,
                  VarianceMP,
                  ProbabilityMP,
                  VnEntropyMP,
                  MutualInfoMP,
                  ShadowExpvalMP,
              ),
          ):
              # Result is a float
              result = self._asarray(result, dtype=self.R_DTYPE)
  
          if self._shot_vector is not None and isinstance(
              result, np.ndarray
          ):
              # In the shot vector case, measurement results may be of shape (N, 1) instead of (N,)
              # Squeeze the result to transform the results
              #
              # E.g.,
              # before:
              # [[0.489]
              #  [0.511]
              #  [0.   ]
              #  [0.   ]]
              #
              # after: [0.489 0.511 0.    0.   ]
              result = qml.math.squeeze(result)
  
          # 3. Append to final list
          if result is not None:
              results.append(result)
  
      return results
  
imports: import abc
  import itertools
  import warnings
  from collections import defaultdict
  from typing import Union, List
  import inspect
  import logging
  import numpy
  import pennylane
  from pennylane import Device, DeviceError
  from pennylane.workflow import set_shots
  from pennylane.math import multiply
  from pennylane.math import sum
  from pennylane.measurements import ClassicalShadowMP, CountsMP, ExpectationMP, MeasurementProcess, MeasurementTransform, MeasurementValue, MutualInfoMP, ProbabilityMP, SampleMeasurement, SampleMP, ShadowExpvalMP, StateMeasurement, StateMP, VarianceMP, VnEntropyMP, Shots
  from pennylane.resource import Resources
  from pennylane.operation import operation_derivative, Operation
  from pennylane.tape import QuantumTape
  from pennylane.wires import Wires
label_desc: Use image node_id 13 for calling the QubitDevice obj's underlying member method code with example usage: obj.statistics(circuit, shot_range, bin_size) and returns: results