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: Return samples of an observable.
  
  Args:
      observable (Observable): the observable to sample
      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.
      counts (bool): whether counts (``True``) or raw samples (``False``)
          should be returned
  
  Raises:
      EigvalsUndefinedError: if no information is available about the
          eigenvalues of the observable
  
  Returns:
      Union[array[float], dict, list[dict]]: samples in an array of
      dimension ``(shots,)`` or counts
docstr_tok: ["Return","samples","of","an","observable",".","Args",":","observable","(","Observable",")",":","the","observable","to","sample","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",".","counts","(","bool",")",":","whether","counts","(","``","True","``",")","or","raw","samples","(","``","False","``",")","should","be","returned","Raises",":","EigvalsUndefinedError",":","if","no","information","is","available","about","the","eigenvalues","of","the","observable","Returns",":","Union","[","array","[","float","]",",","dict",",","list","[","dict","]","]",":","samples","in","an","array","of","dimension","``","(","shots",",",")","``","or","counts"]
code: def sample(
      self, observable, shot_range=None, bin_size=None, counts=False
  ):
      """Return samples of an observable.
  
      Args:
          observable (Observable): the observable to sample
          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.
          counts (bool): whether counts (``True``) or raw samples (``False``)
              should be returned
  
      Raises:
          EigvalsUndefinedError: if no information is available about the
              eigenvalues of the observable
  
      Returns:
          Union[array[float], dict, list[dict]]: samples in an array of
          dimension ``(shots,)`` or counts
      """
      mp = observable
      no_observable_provided = False
      if isinstance(mp, MeasurementProcess):
          if mp.obs is not None:
              observable = mp.obs
          elif mp.mv is not None and isinstance(
              mp.mv, MeasurementValue
          ):
              observable = mp.mv
          else:
              no_observable_provided = True
  
      # translate to wire labels used by device. observable is list when measuring sequence
      # of multiple MeasurementValues
      device_wires = self.map_wires(observable.wires)
      name = observable.name
      # Select the samples from self._samples that correspond to ``shot_range`` if provided
      if shot_range is None:
          sub_samples = self._samples
      else:
          # Indexing corresponds to: (potential broadcasting, shots, wires). Note that the last
          # colon (:) is required because shots is the second-to-last axis and the
          # Ellipsis (...) otherwise would take up broadcasting and shots axes.
          sub_samples = self._samples[..., slice(*shot_range), :]
  
      if isinstance(name, str) and name in {
          "PauliX",
          "PauliY",
          "PauliZ",
          "Hadamard",
      }:
          # Process samples for observables with eigenvalues {1, -1}
          samples = 1 - 2 * sub_samples[..., device_wires[0]]
  
      elif no_observable_provided:
          # if no observable was provided then return the raw samples
          if len(observable.wires) != 0:
              # if wires are provided, then we only return samples from those wires
              samples = sub_samples[..., np.array(device_wires)]
          else:
              samples = sub_samples
  
      else:
          # Replace the basis state in the computational basis with the correct eigenvalue.
          # Extract only the columns of the basis samples required based on ``wires``.
          samples = sub_samples[
              ..., np.array(device_wires)
          ]  # Add np.array here for Jax support.
          powers_of_two = 2 ** np.arange(samples.shape[-1])[::-1]
          indices = samples @ powers_of_two
          indices = np.array(
              indices
          )  # Add np.array here for Jax support.
          if isinstance(observable, MeasurementValue):
              eigvals = self._asarray(
                  [
                      observable[i]
                      for i in range(2 ** len(observable.measurements))
                  ],
                  dtype=self.R_DTYPE,
              )
              samples = eigvals[indices]
          else:
              try:
                  samples = observable.eigvals()[indices]
              except qml.operation.EigvalsUndefinedError as e:
                  # if observable has no info on eigenvalues, we cannot return this measurement
                  raise qml.operation.EigvalsUndefinedError(
                      f"Cannot compute samples of {observable.name}."
                  ) from e
  
      num_wires = (
          len(device_wires) if len(device_wires) > 0 else self.num_wires
      )
      if bin_size is None:
          if counts:
              return self._samples_to_counts(samples, mp, num_wires)
          return samples
  
      if counts:
          shape = (
              (-1, bin_size, num_wires)
              if no_observable_provided
              else (-1, bin_size)
          )
          return [
              self._samples_to_counts(bin_sample, mp, num_wires)
              for bin_sample in samples.reshape(shape)
          ]
  
      return (
          samples.T.reshape((num_wires, bin_size, -1))
          if no_observable_provided
          else samples.reshape((bin_size, -1))
      )
  
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 36 for calling the QubitDevice obj's underlying member method code with example usage: obj.sample(observable, shot_range, bin_size, counts) and returns: unknown, samples, unknown, self