code: def find_constrained_prior(
      distribution: pm.Distribution,
      lower: float,
      upper: float,
      init_guess: Dict[str, float],
      mass: float = 0.95,
      fixed_params: Optional[Dict[str, float]] = None,
      mass_below_lower: Optional[float] = None,
      **kwargs,
  ) -> Dict[str, float]:
      """
      Find optimal parameters to get `mass` % of probability
      of a :ref:`distribution <api_distributions>` between `lower` and `upper`.
  
      Note: only works for one- and two-parameter distributions, as there
      are exactly two constraints. Fix some combination of parameters
      if you want to use it on >=3-parameter distributions.
  
      Parameters
      ----------
      distribution : Distribution
          PyMC distribution you want to set a prior on.
          Needs to have a ``logcdf`` method implemented in PyMC.
      lower : float
          Lower bound to get `mass` % of probability of `pm_dist`.
      upper : float
          Upper bound to get `mass` % of probability of `pm_dist`.
      init_guess : dict of {str : float}
          Initial guess for ``scipy.optimize.least_squares`` to find the
          optimal parameters of `pm_dist` fitting the interval constraint.
          Must be a dictionary with the name of the PyMC distribution's
          parameter as keys and the initial guess as values.
      mass : float, default 0.95
          Share of the probability mass we want between ``lower`` and ``upper``.
          Defaults to 95%.
      fixed_params : str or float, optional, default None
          Only used when `pm_dist` has at least three parameters.
          Dictionary of fixed parameters, so that there are only 2 to optimize.
          For instance, for a StudentT, you fix nu to a constant and get the optimized
          mu and sigma.
      mass_below_lower : float, optional, default None
          The probability mass below the ``lower`` bound. If ``None``,
          defaults to ``(1 - mass) / 2``, which implies that the probability
          mass below the ``lower`` value will be equal to the probability
          mass above the ``upper`` value.
  
      Returns
      -------
      opt_params : dict
          The optimized distribution parameters as a dictionary.
          Dictionary keys are the parameter names and
          dictionary values are the optimized parameter values.
  
      Notes
      -----
      Optional keyword arguments can be passed to ``find_constrained_prior``. These will be
      delivered to the underlying call to :external:py:func:`scipy.optimize.minimize`.
  
      Examples
      --------
      .. code-block:: python
  
          # get parameters obeying constraints
          opt_params = pm.find_constrained_prior(
              pm.Gamma, lower=0.1, upper=0.4, mass=0.75, init_guess={"alpha": 1, "beta": 10}
          )
  
          # use these parameters to draw random samples
          samples = pm.Gamma.dist(**opt_params, size=100).eval()
  
          # use these parameters in a model
          with pm.Model():
              x = pm.Gamma('x', **opt_params)
  
          # specify fixed values before optimization
          opt_params = pm.find_constrained_prior(
              pm.StudentT,
              lower=0,
              upper=1,
              init_guess={"mu": 5, "sigma": 2},
              fixed_params={"nu": 7},
          )
  
      Under some circumstances, you might not want to have the same cumulative
      probability below the ``lower`` threshold and above the ``upper`` threshold.
      For example, you might want to constrain an Exponential distribution to
      find the parameter that yields 90% of the mass below the ``upper`` bound,
      and have zero mass below ``lower``. You can do that with the following call
      to ``find_constrained_prior``
  
      .. code-block:: python
  
          opt_params = pm.find_constrained_prior(
              pm.Exponential,
              lower=0,
              upper=3.,
              mass=0.9,
              init_guess={"lam": 1},
              mass_below_lower=0,
          )
      """
      assert 0.01 <= mass <= 0.99, (
          "This function optimizes the mass of the given distribution +/- "
          f"1%, so `mass` has to be between 0.01 and 0.99. You provided {mass}."
      )
      if mass_below_lower is None:
          mass_below_lower = (1 - mass) / 2
  
      # exit when any parameter is not scalar:
      if np.any(np.asarray(distribution.rv_op.ndims_params) != 0):
          raise NotImplementedError(
              "`pm.find_constrained_prior` does not work with non-scalar parameters yet.\n"
              "Feel free to open a pull request on PyMC repo if you really need this feature."
          )
  
      dist_params = pt.vector("dist_params")
      params_to_optim = {
          arg_name: dist_params[i]
          for arg_name, i in zip(
              init_guess.keys(), range(len(init_guess))
          )
      }
  
      if fixed_params is not None:
          params_to_optim.update(fixed_params)
  
      dist = distribution.dist(**params_to_optim)
  
      try:
          logcdf_lower = pm.logcdf(dist, pm.floatX(lower))
          logcdf_upper = pm.logcdf(dist, pm.floatX(upper))
      except AttributeError:
          raise AttributeError(
              f"You cannot use `find_constrained_prior` with {distribution} -- it doesn't have a logcdf "
              "method yet.\nOpen an issue or, even better, a pull request on PyMC repo if you really "
              "need it."
          )
  
      target = (pt.exp(logcdf_lower) - mass_below_lower) ** 2
      target_fn = pm.pytensorf.compile_pymc(
          [dist_params], target, allow_input_downcast=True
      )
  
      constraint = pt.exp(logcdf_upper) - pt.exp(logcdf_lower)
      constraint_fn = pm.pytensorf.compile_pymc(
          [dist_params], constraint, allow_input_downcast=True
      )
  
      jac: Union[str, Callable]
      constraint_jac: Union[str, Callable]
      try:
          pytensor_jac = pm.gradient(target, [dist_params])
          jac = pm.pytensorf.compile_pymc(
              [dist_params], pytensor_jac, allow_input_downcast=True
          )
          pytensor_constraint_jac = pm.gradient(
              constraint, [dist_params]
          )
          constraint_jac = pm.pytensorf.compile_pymc(
              [dist_params],
              pytensor_constraint_jac,
              allow_input_downcast=True,
          )
      # when PyMC cannot compute the gradient
      except (NotImplementedError, NullTypeGradError):
          jac = "2-point"
          constraint_jac = "2-point"
      cons = optimize.NonlinearConstraint(
          constraint_fn, lb=mass, ub=mass, jac=constraint_jac
      )
  
      opt = optimize.minimize(
          target_fn,
          x0=list(init_guess.values()),
          jac=jac,
          constraints=cons,
          **kwargs,
      )
      if not opt.success:
          raise ValueError(
              f"Optimization of parameters failed.\nOptimization termination details:\n{opt}"
          )
  
      # save optimal parameters
      opt_params = {
          param_name: param_value
          for param_name, param_value in zip(init_guess.keys(), opt.x)
      }
      if fixed_params is not None:
          opt_params.update(fixed_params)
      return opt_params
  
imports: from typing import Callable, Dict, Optional, Union
  import numpy
  import pytensor.tensor
  from pytensor.gradient import NullTypeGradError
  from scipy import optimize
  import pymc
label_desc: Use image node_id 1 for calling a global function with example usage: find_constrained_prior(distribution, lower, upper, init_guess, mass, fixed_params, mass_below_lower) and returns: opt_params