From 6dd4b7b56b3da8c49c8dfd0ca2fb84a7691d4405 Mon Sep 17 00:00:00 2001 From: Michael Clerx Date: Tue, 8 Oct 2019 22:15:09 +0100 Subject: [PATCH 1/3] Made MALA allow repeated ask --- pints/_mcmc/_mala.py | 9 +-------- pints/tests/test_mcmc_mala.py | 37 +++++++++++++++++------------------ 2 files changed, 19 insertions(+), 27 deletions(-) diff --git a/pints/_mcmc/_mala.py b/pints/_mcmc/_mala.py index 433579728..a49e6aa29 100644 --- a/pints/_mcmc/_mala.py +++ b/pints/_mcmc/_mala.py @@ -85,7 +85,6 @@ def __init__(self, x0, sigma0=None): # Set initial state self._running = False - self._ready_for_tell = False # Current point and proposed point self._current = None @@ -166,9 +165,6 @@ def ask(self): if not self._running: self._initialise() - if self._ready_for_tell: - raise RuntimeError('Ask() called when expecting call to tell().') - # Propose new point if self._proposed is None: @@ -187,8 +183,6 @@ def ask(self): # Set as read-only self._proposed.setflags(write=False) - self._ready_for_tell = True - # Return proposed point return self._proposed @@ -196,9 +190,8 @@ def tell(self, reply): """ See :meth:`pints.SingleChainMCMC.tell()`. """ # Check if we had a proposal - if not self._ready_for_tell: + if self._proposed is None: raise RuntimeError('Tell called before proposal was set.') - self._ready_for_tell = False # Unpack reply fx, log_gradient = reply diff --git a/pints/tests/test_mcmc_mala.py b/pints/tests/test_mcmc_mala.py index 0ccf845e3..6c8a795e9 100755 --- a/pints/tests/test_mcmc_mala.py +++ b/pints/tests/test_mcmc_mala.py @@ -15,15 +15,13 @@ from shared import StreamCapture -debug = False - class TestMALAMCMC(unittest.TestCase): """ Tests the basic methods of the MALA MCMC routine. """ - def test_method(self): + def test_short_run(self): # Create log pdf log_pdf = pints.toy.GaussianLogPDF([5, 5], [[4, 1], [1, 3]]) @@ -33,9 +31,6 @@ def test_method(self): sigma = [[3, 0], [0, 3]] mcmc = pints.MALAMCMC(x0, sigma) - # This method needs sensitivities - self.assertTrue(mcmc.needs_sensitivities()) - # Perform short run chain = [] for i in range(100): @@ -53,8 +48,11 @@ def test_method(self): self.assertTrue(mcmc.acceptance_rate() >= 0.0 and mcmc.acceptance_rate() <= 1.0) - mcmc._proposed = [1, 3] - self.assertRaises(RuntimeError, mcmc.tell, (fx, gr)) + def test_needs_sensitivities(self): + + # This method needs sensitivities + mcmc = pints.MALAMCMC(np.array([2, 2])) + self.assertTrue(mcmc.needs_sensitivities()) def test_logging(self): """ @@ -63,14 +61,13 @@ def test_logging(self): log_pdf = pints.toy.GaussianLogPDF([5, 5], [[4, 1], [1, 3]]) x0 = [np.array([2, 2]), np.array([8, 8])] - mcmc = pints.MCMCSampling(log_pdf, 2, x0, method=pints.MALAMCMC) + mcmc = pints.MCMCController(log_pdf, 2, x0, method=pints.MALAMCMC) mcmc.set_max_iterations(5) with StreamCapture() as c: mcmc.run() text = c.text() - self.assertIn('Metropolis-Adjusted Langevin Algorithm (MALA)', - text) + self.assertIn('Metropolis-Adjusted Langevin Algorithm (MALA)', text) self.assertIn(' Accept.', text) def test_flow(self): @@ -80,10 +77,16 @@ def test_flow(self): # Test initial proposal is first point mcmc = pints.MALAMCMC(x0) - self.assertTrue(np.all(mcmc.ask() == mcmc._x0)) + self.assertTrue(np.all(mcmc.ask() == x0)) - # Repeated asks - self.assertRaises(RuntimeError, mcmc.ask) + # Repeated asks return same point + self.assertTrue(np.all(mcmc.ask() == x0)) + self.assertTrue(np.all(mcmc.ask() == x0)) + self.assertTrue(np.all(mcmc.ask() == x0)) + for i in range(5): + mcmc.tell(log_pdf.evaluateS1(mcmc.ask())) + x1 = mcmc.ask() + self.assertTrue(np.all(mcmc.ask() == x1)) # Tell without ask mcmc = pints.MALAMCMC(x0) @@ -105,7 +108,7 @@ def test_flow(self): mcmc._running = True self.assertRaises(RuntimeError, mcmc._initialise) - def test_set_hyper_parameters(self): + def test_hyper_parameters(self): """ Tests the parameter interface for this sampler. """ @@ -134,8 +137,4 @@ def test_set_hyper_parameters(self): if __name__ == '__main__': - print('Add -v for more debug output') - import sys - if '-v' in sys.argv: - debug = True unittest.main() From c211e3343f403dbef77ba4f450f108184f7decbf Mon Sep 17 00:00:00 2001 From: Michael Clerx Date: Tue, 8 Oct 2019 22:15:35 +0100 Subject: [PATCH 2/3] Updated test for MALA --- pints/tests/test_mcmc_mala.py | 43 ++++++++++++++++------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/pints/tests/test_mcmc_mala.py b/pints/tests/test_mcmc_mala.py index 6c8a795e9..7f8f9297c 100755 --- a/pints/tests/test_mcmc_mala.py +++ b/pints/tests/test_mcmc_mala.py @@ -22,6 +22,7 @@ class TestMALAMCMC(unittest.TestCase): """ def test_short_run(self): + # Test a short run with MALA # Create log pdf log_pdf = pints.toy.GaussianLogPDF([5, 5], [[4, 1], [1, 3]]) @@ -45,19 +46,17 @@ def test_short_run(self): chain = np.array(chain) self.assertEqual(chain.shape[0], 50) self.assertEqual(chain.shape[1], len(x0)) - self.assertTrue(mcmc.acceptance_rate() >= 0.0 and - mcmc.acceptance_rate() <= 1.0) + self.assertTrue(0 <= mcmc.acceptance_rate() <= 1.0) def test_needs_sensitivities(self): - # This method needs sensitivities + mcmc = pints.MALAMCMC(np.array([2, 2])) self.assertTrue(mcmc.needs_sensitivities()) def test_logging(self): - """ - Test logging includes name and custom fields. - """ + # Test logging includes name and custom fields. + log_pdf = pints.toy.GaussianLogPDF([5, 5], [[4, 1], [1, 3]]) x0 = [np.array([2, 2]), np.array([8, 8])] @@ -71,6 +70,7 @@ def test_logging(self): self.assertIn(' Accept.', text) def test_flow(self): + # Test the ask-and-tell flow log_pdf = pints.toy.GaussianLogPDF([5, 5], [[4, 1], [1, 3]]) x0 = np.array([2, 2]) @@ -88,7 +88,7 @@ def test_flow(self): x1 = mcmc.ask() self.assertTrue(np.all(mcmc.ask() == x1)) - # Tell without ask + # Tell without ask should fail mcmc = pints.MALAMCMC(x0) self.assertRaises(RuntimeError, mcmc.tell, 0) @@ -109,29 +109,26 @@ def test_flow(self): self.assertRaises(RuntimeError, mcmc._initialise) def test_hyper_parameters(self): - """ - Tests the parameter interface for this sampler. - """ - x0 = np.array([2, 2]) - mcmc = pints.MALAMCMC(x0) - self.assertTrue(np.array_equal( - mcmc._scale_vector, - np.diag(mcmc._sigma0)) - ) - self.assertTrue(np.array_equal(mcmc.epsilon(), - 0.2 * np.diag(mcmc._sigma0))) + # Tests the parameter interface for this sampler. + mcmc = pints.MALAMCMC(np.array([2, 2])) self.assertEqual(mcmc.n_hyper_parameters(), 1) mcmc.set_hyper_parameters([[3, 2]]) self.assertTrue(np.array_equal(mcmc.epsilon(), [3, 2])) + mcmc.set_hyper_parameters([[5, 5]]) + self.assertTrue(np.array_equal(mcmc.epsilon(), [5, 5])) - mcmc._step_size = 5 - mcmc._scale_vector = np.array([3, 7]) - mcmc._epsilon = None + def test_epsilon(self): + # Test the epsilon methods + + mcmc = pints.MALAMCMC(np.array([2, 2]), np.array([3, 3])) mcmc.set_epsilon() - self.assertTrue(np.array_equal(mcmc.epsilon(), [15, 35])) + x = mcmc.epsilon() + self.assertAlmostEqual(x[0], 0.6) + self.assertAlmostEqual(x[1], 0.6) mcmc.set_epsilon([0.4, 0.5]) - self.assertTrue(np.array_equal(mcmc.epsilon(), [0.4, 0.5])) + self.assertTrue(np.all(mcmc.epsilon() == [0.4, 0.5])) + self.assertRaises(ValueError, mcmc.set_epsilon, 3.0) self.assertRaises(ValueError, mcmc.set_epsilon, [-2.0, 1]) From 17feb6e37fe1d49e7a2792f8491a1f2ac9c4770a Mon Sep 17 00:00:00 2001 From: Michael Clerx Date: Mon, 8 Feb 2021 12:26:35 +0000 Subject: [PATCH 3/3] Update _mala.py --- pints/_mcmc/_mala.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pints/_mcmc/_mala.py b/pints/_mcmc/_mala.py index 82502b9b8..fabcfc4ff 100644 --- a/pints/_mcmc/_mala.py +++ b/pints/_mcmc/_mala.py @@ -114,7 +114,7 @@ def acceptance_rate(self): Returns the current (measured) acceptance rate. """ return self._acceptance - + def ask(self): """ See :meth:`SingleChainMCMC.ask()`. """ # Initialise on first call