nsamples=5000) print(x.samples.shape) plt.figure() plt.plot(x.samples[:, 0], x.samples[:, 1], 'o') plt.show() # %% md # DREAM algorithm: compare with :class:`.MetropolisHastings` (inputs parameters are set as their default values) # --------------------------------------------------------------------------------------------------------------------- # %% # Define a function to sample seed uniformly distributed in the 2d space ([-20, 20], [-4, 4]) prior_sample = lambda nsamples: np.array([[-2, -2]]) + np.array( [[4, 4]]) * JointIndependent([Uniform(), Uniform()]).rvs(nsamples=nsamples) fig, ax = plt.subplots(ncols=2, figsize=(12, 4)) seed = prior_sample(nsamples=7) x = MetropolisHastings(dimension=2, burn_length=500, jump=50, seed=seed.tolist(), log_pdf_target=log_Rosenbrock, nsamples=1000) ax[0].plot(x.samples[:, 0], x.samples[:, 1], 'o') x = DREAM(dimension=2, burn_length=500, jump=50,
rstate = np.random.RandomState(123) data_noisy = data_clean + rstate.randn(*data_clean.shape) from scipy.stats import norm def log_target(x, data, x_domain): log_target_value = np.zeros(x.shape[0]) for i, xx in enumerate(x): h_xx = xx[0] * x_domain + xx[1] * x_domain**2 log_target_value[i] = np.sum( [norm.logpdf(hxi - datai) for hxi, datai in zip(h_xx, data)]) return log_target_value proposal = JointIndependent([Normal(scale=0.1), Normal(scale=0.05)]) sampler = MetropolisHastings(nsamples=500, dimension=2, log_pdf_target=log_target, burn_length=10, jump=10, n_chains=1, args_target=(data_noisy, domain), proposal=proposal) print(sampler.samples.shape) samples = sampler.samples plt.plot(samples[:, 0], samples[:, 1], 'o', alpha=0.5) plt.plot(1., 2., marker='x', color='orange')
return x**2 + y**2 # %% md # # Create a distribution object, generate samples and evaluate the function at the samples. # %% np.random.seed(1) dist_1 = Uniform(loc=-5.12, scale=10.24) dist_2 = Uniform(loc=-5.12, scale=10.24) marg = [dist_1, dist_2] joint = JointIndependent(marginals=marg) n_samples = 100 x = joint.rvs(n_samples) y = function(x[:, 0], x[:, 1]) # %% md # # Visualize the 2D function. # %% xmin, xmax = -6, 6 ymin, ymax = -6, 6 X1 = np.linspace(xmin, xmax, 50) X2 = np.linspace(ymin, ymax, 50)
for n in range(1, Ne + 1)] ev = np.array(ev) return ev[:Ne] # %% md # # Create a distribution object. # %% pdf_lx = Normal(loc=2, scale=0.02) pdf_ly = Normal(loc=1, scale=0.01) margs = [pdf_lx, pdf_ly] joint = JointIndependent(marginals=margs) # %% md # # Define the number of input dimensions and choose the number of output dimensions (number of eigenvalues). # %% dim_in = 2 dim_out = 10 # %% md # # Construct PCE models by varying the maximum degree of polynomials (and therefore the number of polynomial basis) and # compute the validation error for all resulting models.
def __init__( self, pdf_target: Union[Callable, list[Callable]] = None, log_pdf_target: Union[Callable, list[Callable]] = None, args_target: tuple = None, burn_length: Annotated[int, Is[lambda x: x >= 0]] = 0, jump: int = 1, dimension: int = None, seed: list = None, save_log_pdf: bool = False, concatenate_chains: bool = True, n_chains: int = None, proposal: Distribution = None, proposal_is_symmetric: bool = False, random_state: RandomStateType = None, nsamples: PositiveInteger = None, nsamples_per_chain: PositiveInteger = None, ): """ Metropolis-Hastings algorithm :cite:`MCMC1` :cite:`MCMC2` :param pdf_target: Target density function from which to draw random samples. Either `pdf_target` or `log_pdf_target` must be provided (the latter should be preferred for better numerical stability). If `pdf_target` is a callable, it refers to the joint pdf to sample from, it must take at least one input **x**, which are the point(s) at which to evaluate the pdf. Within :class:`.MCMC` the pdf_target is evaluated as: :code:`p(x) = pdf_target(x, \*args_target)` where **x** is a :class:`numpy.ndarray of shape :code:`(nsamples, dimension)` and `args_target` are additional positional arguments that are provided to :class:`.MCMC` via its `args_target` input. If `pdf_target` is a list of callables, it refers to independent marginals to sample from. The marginal in dimension :code:`j` is evaluated as: :code:`p_j(xj) = pdf_target[j](xj, \*args_target[j])` where **x** is a :class:`numpy.ndarray` of shape :code:`(nsamples, dimension)` :param log_pdf_target: Logarithm of the target density function from which to draw random samples. Either `pdf_target` or `log_pdf_target` must be provided (the latter should be preferred for better numerical stability). Same comments as for input `pdf_target`. :param args_target: Positional arguments of the pdf / log-pdf target function. See `pdf_target` :param burn_length: Length of burn-in - i.e., number of samples at the beginning of the chain to discard (note: no thinning during burn-in). Default is :math:`0`, no burn-in. :param jump: Thinning parameter, used to reduce correlation between samples. Setting :code:`jump=n` corresponds to skipping :code:`n-1` states between accepted states of the chain. Default is :math:`1` (no thinning). :param dimension: A scalar value defining the dimension of target density function. Either `dimension` and `n_chains` or `seed` must be provided. :param seed: Seed of the Markov chain(s), shape :code:`(n_chains, dimension)`. Default: :code:`zeros(n_chains x dimension)`. If seed is not provided, both n_chains and dimension must be provided. :param save_log_pdf: Boolean that indicates whether to save log-pdf values along with the samples. Default: :any:`False` :param concatenate_chains: Boolean that indicates whether to concatenate the chains after a run, i.e., samples are stored as an :class:`numpy.ndarray` of shape :code:`(nsamples * n_chains, dimension)` if :any:`True`, :code:`(nsamples, n_chains, dimension)` if :any:`False`. Default: :any:`True` :param n_chains: The number of Markov chains to generate. Either dimension and `n_chains` or `seed` must be provided. :param proposal: Proposal distribution, must have a log_pdf/pdf and rvs method. Default: standard multivariate normal :param proposal_is_symmetric: Indicates whether the proposal distribution is symmetric, affects computation of acceptance probability alpha Default: :any:`False`, set to :any:`True` if default proposal is used :param random_state: Random seed used to initialize the pseudo-random number generator. Default is :any:`None`. :param nsamples: Number of samples to generate. :param nsamples_per_chain: Number of samples to generate per chain. """ self.nsamples = nsamples self.nsamples_per_chain = nsamples_per_chain super().__init__( pdf_target=pdf_target, log_pdf_target=log_pdf_target, args_target=args_target, dimension=dimension, seed=seed, burn_length=burn_length, jump=jump, save_log_pdf=save_log_pdf, concatenate_chains=concatenate_chains, random_state=random_state, n_chains=n_chains, ) self.logger = logging.getLogger(__name__) # Initialize algorithm specific inputs self.proposal = proposal self.proposal_is_symmetric = proposal_is_symmetric if self.proposal is None: if self.dimension is None: raise ValueError( "UQpy: Either input proposal or dimension must be provided." ) from UQpy.distributions import JointIndependent, Normal self.proposal = JointIndependent([Normal()] * self.dimension) self.proposal_is_symmetric = True else: self._check_methods_proposal(self.proposal) self.logger.info("\nUQpy: Initialization of " + self.__class__.__name__ + " algorithm complete.") if (nsamples is not None) or (nsamples_per_chain is not None): self.run( nsamples=nsamples, nsamples_per_chain=nsamples_per_chain, )
# where :math:`\tilde{w}` are the normalized weights. # # [1] *Sequential Monte Carlo Methods in Practice*, A. Doucet, N. de Freitas, and N. Gordon, 2001, Springer, New York from UQpy.distributions import Uniform, JointIndependent from UQpy.sampling import ImportanceSampling import matplotlib.pyplot as plt import numpy as np def log_Rosenbrock(x, param): return (-(100 * (x[:, 1] - x[:, 0]**2)**2 + (1 - x[:, 0])**2) / param) proposal = JointIndependent( [Uniform(loc=-8, scale=16), Uniform(loc=-10, scale=60)]) print(proposal.get_parameters()) w = ImportanceSampling(log_pdf_target=log_Rosenbrock, args_target=(20, ), proposal=proposal, nsamples=5000) #%% md # # Look at distribution of weights # ------------------------------- #%%
h_func.run(samples=param_true) # Add noise error_covariance = 1. data_clean = np.array(h_func.qoi_list[0]) noise = Normal(loc=0., scale=np.sqrt(error_covariance)).rvs(nsamples=50).reshape( (50, )) data_3 = data_clean + noise print('Shape of data: {}'.format(data_3.shape)) inference_model = ComputationalModel(n_parameters=2, runmodel_object=h_func, error_covariance=error_covariance) sampling = ImportanceSampling(proposal=JointIndependent([Normal(scale=2, )] * 2)) bayes_estimator =\ BayesParameterEstimation(inference_model=inference_model, data=data_3, sampling_class=sampling, nsamples=5000) s = bayes_estimator.sampler.samples w = bayes_estimator.sampler.weights print(sum(w)) # print results fig, ax = plt.subplots(1, 2) for i in range(2): ax[i].hist(x=s[:, i], weights=None,
import numpy as np from UQpy.sampling import ImportanceSampling from UQpy.distributions import JointIndependent, Uniform def log_rosenbrock(x, param): return -(100 * (x[:, 1] - x[:, 0]**2)**2 + (1 - x[:, 0])**2) / param def rosenbrock(x): return np.exp(-(100 * (x[:, 1] - x[:, 0]**2)**2 + (1 - x[:, 0])**2) / 20) proposal = JointIndependent( [Uniform(loc=-8, scale=16), Uniform(loc=-10, scale=60)]) proposal2 = JointIndependent( [Uniform(loc=-8, scale=16), Uniform(loc=-10, scale=60)]) del proposal2.log_pdf def test_pdf_target(): w = ImportanceSampling(pdf_target=rosenbrock, proposal=proposal, random_state=123, nsamples=2000) assert (w.weights.shape == (2000, ) and np.all(np.round(w.samples[-1], 3) == [-6.434, 27.373]))
print('posterior probabilities of all three models') print(model_posterior_probas) #%% md # # Define the models for use in UQpy #%% candidate_models = [] for n, model_name in enumerate(model_names): run_model = RunModel(model_script='local_pfn_models.py', model_object_name=model_name, vec=False) prior = JointIndependent([ Normal(loc=m, scale=std) for m, std in zip(model_prior_means[n], model_prior_stds[n]) ]) model = ComputationalModel(n_parameters=model_n_params[n], runmodel_object=run_model, prior=prior, error_covariance=error_covariance, name=model_name) candidate_models.append(model) #%% md # # Run MCMC for one model #%% # Quadratic model
linewidth=2, color='r') plt.title('data as histogram and true distribution to be estimated') plt.show() #%% md # # In a Bayesian setting, the definition of a prior pdf is a key point. The prior for the parameters must be defined in # the model. Note that if no prior is given, an improper, uninformative, prior is chosen, :math:`p(\theta)=1` for all # :math:`\theta`. #%% p0 = Uniform(loc=0., scale=15) p1 = Lognormal(s=1., loc=0., scale=1.) prior = JointIndependent(marginals=[p0, p1]) candidate_model = DistributionModel(distributions=Normal(loc=None, scale=None), n_parameters=2, prior=prior) # Learn the unknown parameters using MCMC from UQpy.sampling import MetropolisHastings mh1 = MetropolisHastings(jump=10, burn_length=10, seed=[1.0, 0.2], random_state=123) bayes_estimator = BayesParameterEstimation(inference_model=candidate_model, data=data_1,
def function(x): return 100 * (np.exp(-2 / (x[:, 0]**1.75)) + np.exp(-2 / (x[:, 1]**1.5)) + np.exp(-2 / (x[:, 2]**1.25))) # %% md # # Define the input probability distributions. # %% # input distributions dist = Uniform(loc=0, scale=1) marg = [dist] * 3 joint = JointIndependent(marginals=marg) # %% md # # Compute reference mean and variance values using Monte Carlo sampling. # %% # reference moments via Monte Carlo Sampling n_samples_mc = 1000000 xx = joint.rvs(n_samples_mc) yy = function(xx) mean_ref = yy.mean() var_ref = yy.var() # %% md
from UQpy.sampling import ImportanceSampling #%% md # # First data is generated from a true model. A distribution with copulas does not possess a fit method, thus sampling is # performed using importance sampling/resampling. #%% # dist_true exhibits dependence between the two dimensions, defined using a gumbel copula dist_true = JointCopula(marginals=[Normal(), Normal()], copula=Gumbel(theta=2.)) # generate data using importance sampling: sample from a bivariate gaussian without copula, then weight samples u = ImportanceSampling( proposal=JointIndependent(marginals=[Normal(), Normal()]), log_pdf_target=dist_true.log_pdf, nsamples=500) print(u.samples.shape) print(u.weights.shape) # Resample to obtain 5,000 data points u.resample(nsamples=5000) data_2 = u.unweighted_samples print('Shape of data: {}'.format(data_2.shape)) fig, ax = plt.subplots() ax.scatter(data_2[:, 0], data_2[:, 1], alpha=0.2) ax.set_title( 'Data points from true bivariate normal with gumbel dependency structure') plt.show()
# %% md # # Plot the failure domain # %% x = np.linspace(2, 8, 1000) y = np.linspace(25, 225, 1000) X, Y = np.meshgrid(x, y) Z = np.zeros((1000, 1000)) d1 = Normal(loc=5, scale=1) d2 = Normal(loc=125, scale=20) dist = JointIndependent(marginals=[d1, d2]) for i in range(len(x)): Z[i, :] = dist.pdf( np.append(np.atleast_2d(X[i, :]), np.atleast_2d(Y[i, :]), 0).T) fig, ax = plt.subplots() CS = ax.contour(X, Y, Z, 15) plt.plot(m, k_hi, 'k') plt.plot(m, k_lo, 'k') # plt.fill_between(m,k_lo,k_hi) plt.xlim([mu_m - 3 * sigma_m, mu_m + 3 * sigma_m]) plt.ylim([mu_k - 3 * sigma_k, mu_k + 3 * sigma_k]) plt.xlabel(r'Mass ($m$)') plt.ylabel(r'Stiffness ($k$)') plt.grid(True)
data_clean = np.array(h_func.qoi_list[0]) print(data_clean.shape) # Add noise, use a RandomState for reproducible results error_covariance = 1. noise = Normal(loc=0., scale=np.sqrt(error_covariance)).rvs(nsamples=50, random_state=123).reshape( (50, )) data_3 = data_clean + noise print('Shape of data: {}'.format(data_3.shape)) print(data_3[:4]) p0 = Normal() p1 = Normal() prior = JointIndependent(marginals=[p0, p1]) inference_model = ComputationalModel(n_parameters=2, runmodel_object=h_func, error_covariance=error_covariance, prior=prior) proposal = JointIndependent([Normal(scale=0.1), Normal(scale=0.05)]) mh1 = MetropolisHastings(jump=10, burn_length=0, proposal=proposal, seed=[0.5, 2.5], random_state=456) bayes_estimator = BayesParameterEstimation(inference_model=inference_model,
from UQpy.distributions import Normal, JointIndependent from UQpy.distributions import Gumbel, JointCopula #%% md # # Define a Copula # --------------- # The definition of bivariate distribution with a copula, is similar to defining a multivariate distribution from # independent marginals. In both cases a list of marginals needs to be defined. In case of #%% marginals = [Normal(loc=0., scale=1), Normal(loc=0., scale=1)] copula = Gumbel(theta=3.) # dist_1 is a multivariate normal with independent marginals dist_1 = JointIndependent(marginals) print('Does the distribution with independent marginals have an rvs method?') print(hasattr(dist_1, 'rvs')) # dist_2 exhibits dependence between the two dimensions, defined using a gumbel copula dist_2 = JointCopula(marginals=marginals, copula=copula) print('Does the distribution with copula have an rvs method?') print(hasattr(dist_2, 'rvs')) #%% md # # Plot the pdf of the distribution before and after the copula # ------------------------------------------------------------- # #%%
print(seed) print(x.samples[0, :, :]) # %% md # # The algorithm-specific parameters for MetropolisHastings are proposal and proposal_is_symmetric # ------------------------------------------------------------------------------------------------- # The default proposal is standard normal (symmetric). # %% # Define a few proposals to try out from UQpy.distributions import JointIndependent, Normal, Uniform proposals = [ JointIndependent([Normal(), Normal()]), JointIndependent( [Uniform(loc=-0.5, scale=1.5), Uniform(loc=-0.5, scale=1.5)]), Normal() ] proposals_is_symmetric = [True, False, False] fig, ax = plt.subplots(ncols=3, figsize=(16, 4)) for i, (proposal, symm) in enumerate(zip(proposals, proposals_is_symmetric)): print(i) try: x = MetropolisHastings(dimension=2, burn_length=500, jump=100,