code: def check_logp(
      pymc_dist: Distribution,
      domain: Domain,
      paramdomains: Dict[str, Domain],
      scipy_logp: Callable,
      decimal: Optional[int] = None,
      n_samples: int = 100,
      extra_args: Optional[Dict[str, Any]] = None,
      scipy_args: Optional[Dict[str, Any]] = None,
      skip_paramdomain_outside_edge_test: bool = False,
  ) -> None:
      """
      Generic test for PyMC logp methods
  
      Test PyMC logp and equivalent scipy logpmf/logpdf methods give similar
      results for valid values and parameters inside the supported edges.
      Edges are excluded by default, but can be artificially included by
      creating a domain with repeated values (e.g., `Domain([0, 0, .5, 1, 1]`)
  
      Parameters
      ----------
      pymc_dist: PyMC distribution
      domain : Domain
          Supported domain of distribution values
      paramdomains : Dictionary of Parameter : Domain pairs
          Supported domains of distribution parameters
      scipy_logp : Scipy logpmf/logpdf method
          Scipy logp method of equivalent pymc_dist distribution
      decimal : Int
          Level of precision with which pymc_dist and scipy logp are compared.
          Defaults to 6 for float64 and 3 for float32
      n_samples : Int
          Upper limit on the number of valid domain and value combinations that
          are compared between pymc and scipy methods. If n_samples is below the
          total number of combinations, a random subset is evaluated. Setting
          n_samples = -1, will return all possible combinations. Defaults to 100
      extra_args : Dictionary with extra arguments needed to build pymc model
          Dictionary is passed to helper function `build_model` from which
          the pymc distribution logp is calculated
      scipy_args : Dictionary with extra arguments needed to call scipy logp method
          Usually the same as extra_args
      """
      if decimal is None:
          decimal = select_by_precision(float64=6, float32=3)
  
      if scipy_args is None:
          scipy_args = {}
  
      def scipy_logp_with_scipy_args(**args):
          args.update(scipy_args)
          return scipy_logp(**args)
  
      dist = create_dist_from_paramdomains(
          pymc_dist, paramdomains, extra_args
      )
      value = dist.type()
      value.name = "value"
      pymc_dist_logp = logp(dist, value).sum()
      pymc_logp = pytensor.function(
          list(inputvars(pymc_dist_logp)), pymc_dist_logp
      )
  
      # Test supported value and parameters domain matches Scipy
      domains = paramdomains.copy()
      domains["value"] = domain
      for point in product(domains, n_samples=n_samples):
          point = dict(point)
          npt.assert_almost_equal(
              pymc_logp(**point),
              scipy_logp_with_scipy_args(**point),
              decimal=decimal,
              err_msg=str(point),
          )
  
      valid_value = domain.vals[0]
      valid_params = {
          param: paramdomain.vals[0]
          for param, paramdomain in paramdomains.items()
      }
      valid_params["value"] = valid_value
  
      # Test pymc distribution raises ParameterValueError for scalar parameters outside
      # the supported domain edges (excluding edges)
      if not skip_paramdomain_outside_edge_test:
          invalid_params = find_invalid_scalar_params(paramdomains)
  
          for invalid_param, invalid_edges in invalid_params.items():
              for invalid_edge in invalid_edges:
                  if invalid_edge is None:
                      continue
  
                  point = (
                      valid_params.copy()
                  )  # Shallow copy should be okay
                  point[invalid_param] = invalid_edge
                  with pytest.raises(ParameterValueError):
                      pymc_logp(**point)
                      pytest.fail(f"test_params={point}")
  
      # Test that values outside of scalar domain support evaluate to -np.inf
      invalid_values = find_invalid_scalar_params({"value": domain})[
          "value"
      ]
  
      for invalid_value in invalid_values:
          if invalid_value is None:
              continue
  
          point = valid_params.copy()
          point["value"] = invalid_value
          npt.assert_equal(
              pymc_logp(**point),
              -np.inf,
              err_msg=str(point),
          )
  
imports: import functools
  import itertools
  from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union
  import numpy
  import pytensor
  import pytensor.tensor
  import pytest
  from numpy import random
  from numpy import testing
  from pytensor.compile.mode import Mode
  from pytensor.graph.basic import Variable
  from pytensor.graph.rewriting.basic import in2out
  from pytensor.tensor import TensorVariable
  from scipy import special
  from scipy import stats
  import pymc
  from pymc.distributions.distribution import Distribution
  from pymc.distributions.shape_utils import change_dist_size
  from pymc.initial_point import make_initial_point_fn
  from pymc.logprob.basic import icdf, logcdf, logp, transformed_conditional_logp
  from pymc.logprob.utils import ParameterValueError, local_check_parameter_to_ninf_switch, rvs_in_graph
  from pymc.pytensorf import compile_pymc, floatX, inputvars, intX
label_desc: Use image node_id 14 for calling a global function with example usage: check_logp(pymc_dist, domain, paramdomains, scipy_logp, decimal, n_samples, extra_args, scipy_args, skip_paramdomain_outside_edge_test) and returns: scipy_logp