Source code for preliz.predictive.ppe

"""Projective predictive elicitation."""

import warnings

import numpy as np

from preliz.internal.optimization import optimize_pymc_model
from preliz.ppls.agnostic import get_engine
from preliz.ppls.bambi_io import get_pymc_model, write_bambi_string
from preliz.ppls.pymc_io import (
    back_fitting_pymc,
    compile_mllk,
    extract_preliz_distributions,
    get_initial_guess,
    retrieve_variable_info,
    unravel_projection,
    write_pymc_string,
)


[docs] def ppe(model, target, engine="auto", new_families=None, random_state=0): """ Prior Predictive Elicitation. This method is experimental and under development. It does not offers guarantees of correctness. Use with caution and triple-check the results. With the projective method we attempt to find a prior that induces a prior predictive distribution as close as possible to the target distribution Parameters ---------- model : a probabilistic model Currently it only works with PyMC model. More PPls coming soon. target : a PreliZ distribution or list Instance of a PreliZ distribution or a list of tuples where each tuple contains a PreliZ distribution and a weight. This represents the prior predictive distribution **previously** elicited by the user, possibly using other PreliZ's methods to obtain this distribution, such as maxent, roulette, quartile, etc. This should represent the domain-knowledge of the user and not any observed dataset. engine : str Library used to define the model. Either `"auto"` (default), `"pymc"` or `"bambi"`. Ig `"auto"`, the library is automatically detected. new_families : "auto", list or dict Defaults to None, the samples are fit to the original prior distribution. If "auto", the method evaluates the fit to the original prior plus a set of predefined distributions. Use a list of PreliZ distribution to specify the alternative distributions you want to consider. Use a dict with variables names in ``model`` as keys and a list of PreliZ distributions as values. This allows to specify alternative distributions per variable. random_state : {None, int, numpy.random.Generator, numpy.random.RandomState} Defaults to 0. Ignored if `method` is `"pathfinder"`. Returns ------- new_priors : str A string representation of the new priors. The user can copy and paste it into the model's code. Ideally, with none to minimal changes. """ warnings.warn( """This method is experimental and under development with no guarantees of correctness. Use with caution and triple-check the results.""" ) opt_iterations = 400 rng = np.random.default_rng(random_state) engine = get_engine(model) if engine == "auto" else engine # Get models information if engine == "bambi": model = get_pymc_model(model) preliz_model = extract_preliz_distributions(model) var_info, num_draws = retrieve_variable_info(model) # Initial point for optimization initial_guess = get_initial_guess(model) # compile PyMC model fmodel, old_y_value, obs_rvs = compile_mllk(model) projection_raveled = optimize_pymc_model( fmodel, target, num_draws, opt_iterations, initial_guess, rng, ) # restore obs_rvs value in the model model.rvs_to_values[obs_rvs] = old_y_value projection_unraveled = unravel_projection(projection_raveled, var_info, opt_iterations) # Backfit `projected_posterior` into the model's prior-families projection_backfitted = back_fitting_pymc( projection_unraveled, preliz_model, var_info, new_families ) if engine == "bambi": new_priors = write_bambi_string(projection_backfitted, var_info) elif engine == "pymc": new_priors = write_pymc_string(projection_backfitted, var_info) return new_priors