From 94ab93a771b7608ab198fd251b4671b57b6da50d Mon Sep 17 00:00:00 2001 From: Oded Stein Date: Thu, 30 Nov 2023 18:25:34 -0800 Subject: [PATCH] Make metropolis_hastings seedable (#106) * Make metropolis_hastings seedable * switching order of parameters so as to not break old code. --- src/gpytoolbox/metropolis_hastings.py | 11 ++++++++--- test/test_metropolis_hastings.py | 8 ++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/gpytoolbox/metropolis_hastings.py b/src/gpytoolbox/metropolis_hastings.py index da086de6..e45cffc8 100644 --- a/src/gpytoolbox/metropolis_hastings.py +++ b/src/gpytoolbox/metropolis_hastings.py @@ -1,7 +1,10 @@ import numpy as np -import random -def metropolis_hastings(unnorm_distr, next_sample, x0 , num_samples=100): +def metropolis_hastings(unnorm_distr, + next_sample, + x0, + num_samples=100, + rng=np.random.default_rng()): """Randomly sample according to an unnormalized distribution. Given a function which is proportional to a probabilistic density and a strategy for generating candidate points, returns a set of samples which will asymptotically tend to being a sample a random sample of the unknown distribution. @@ -16,6 +19,8 @@ def metropolis_hastings(unnorm_distr, next_sample, x0 , num_samples=100): Initial sample num_samples : int Number of samples in output (this will be *more* than the total number of considered samples or evaluations of unnorm_distr) + rng : numpy rng, optional (default: new `np.random.default_rng()`) + which numpy random number generator to use Returns ------- @@ -59,7 +64,7 @@ def unnorm_distr(x): f1 = unnorm_distr(x1) #print(f1) # Generate random value between 0 and 1 - r = random.uniform(0, 1) + r = rng.uniform(0, 1) #print(f1/f0) if r<(f1/f0): # If accepted, update current sample with candidate sample diff --git a/test/test_metropolis_hastings.py b/test/test_metropolis_hastings.py index fc8476a6..d13d4932 100644 --- a/test/test_metropolis_hastings.py +++ b/test/test_metropolis_hastings.py @@ -7,7 +7,7 @@ class TestMetropolisHastings(unittest.TestCase): def test_analytic_1d(self): - np.random.seed(0) + rng = np.random.default_rng(4352) # 1D test # Sample next point from a normal distribution def next_sample(x0): @@ -18,7 +18,7 @@ def next_sample(x0): def unnorm_distr(x): return np.max((1-np.abs(x[0]),1e-8)) - S, F = gpytoolbox.metropolis_hastings(unnorm_distr,next_sample,np.array([0.1]),1000000) + S, F = gpytoolbox.metropolis_hastings(unnorm_distr,next_sample,np.array([0.1]),1000000,rng) # This should look like an absolute value pyramid function hist, bin_edges = np.histogram(S,bins=np.linspace(-1,1,101), density=True) bin_centers = (bin_edges[0:100] + bin_edges[1:101])/2. @@ -30,7 +30,7 @@ def unnorm_distr(x): # plt.show(block=False) def test_analytic_2d(self): - np.random.seed(0) + rng = np.random.default_rng(8312) # plt.pause(10) # plt.close(plot1) # 2D test @@ -42,7 +42,7 @@ def next_sample(x0): def unnorm_distr(x): return 100*multivariate_normal.pdf(x,mean=np.array([0.0,0.0]),cov=np.array([[0.01,0.0],[0.0,0.01]])) - S, F = gpytoolbox.metropolis_hastings(unnorm_distr,next_sample,np.array([0.01,0.01]),500000) + S, F = gpytoolbox.metropolis_hastings(unnorm_distr,next_sample,np.array([0.01,0.01]),500000,rng) nbins = 40 H, xedges, yedges = np.histogram2d(S[:,0], S[:,1], density=True, bins=nbins)