def test_format(self): # Test the format() method. t = pints.Timer() self.assertEqual(t.format(1e-3), '0.001 seconds') self.assertEqual(t.format(0.000123456789), '0.000123456789 seconds') self.assertEqual(t.format(0.123456789), '0.12 seconds') if sys.hexversion < 0x3000000: self.assertEqual(t.format(2), '2.0 seconds') else: self.assertEqual(t.format(2), '2 seconds') self.assertEqual(t.format(2.5), '2.5 seconds') self.assertEqual(t.format(12.5), '12.5 seconds') self.assertEqual(t.format(59.41), '59.41 seconds') self.assertEqual(t.format(59.4126347547), '59.41 seconds') self.assertEqual(t.format(60.2), '1 minute, 0 seconds') self.assertEqual(t.format(61), '1 minute, 1 second') self.assertEqual(t.format(121), '2 minutes, 1 second') self.assertEqual( t.format(604800), '1 week, 0 days, 0 hours, 0 minutes, 0 seconds') self.assertEqual( t.format(2 * 604800 + 3 * 3600 + 60 + 4), '2 weeks, 0 days, 3 hours, 1 minute, 4 seconds') # Test without argument self.assertIsInstance(t.format(), basestring)
def test_post_run_statistics(self): # Test the methods to return statistics, post-run. r = pints.toy.TwistedGaussianLogPDF(2, 0.01) x = np.array([0, 1.01]) b = pints.RectangularBoundaries([-0.01, 0.95], [0.01, 1.05]) s = 0.01 opt = pints.OptimisationController(r, x, s, b, method=method) opt.set_log_to_screen(False) opt.set_max_unchanged_iterations(50, 1e-11) np.random.seed(123) # Before run methods return None self.assertIsNone(opt.iterations()) self.assertIsNone(opt.evaluations()) self.assertIsNone(opt.time()) t = pints.Timer() opt.run() t_upper = t.time() self.assertEqual(opt.iterations(), 75) self.assertEqual(opt.evaluations(), 450) # Time after run is greater than zero self.assertIsInstance(opt.time(), float) self.assertGreater(opt.time(), 0) self.assertGreater(t_upper, opt.time())
def _initialise_logger(self): """ Initialises logger. """ # Start logging self._logging = self._log_to_screen or self._log_filename if self._logging: # Create timer self._timer = pints.Timer() if self._log_to_screen: # Show current settings print('Running ' + self._sampler.name()) print('Number of active points: ' + str(self._n_active_points)) print('Total number of iterations: ' + str(self._iterations)) print('Total number of posterior samples: ' + str(self._posterior_samples)) # Set up logger self._logger = pints.Logger() if not self._log_to_screen: self._logger.set_stream(None) if self._log_filename: self._logger.set_filename(self._log_filename, csv=self._log_csv) # Add fields to log self._logger.add_counter('Iter.', max_value=self._iterations) self._logger.add_counter('Eval.', max_value=self._iterations * 10) self._logger.add_time('Time m:s') self._logger.add_float('Delta_log(z)') self._logger.add_float('Acceptance rate')
def test_timing(self): """ Test the time() and reset() methods. """ t = pints.Timer() a = t.time() self.assertGreaterEqual(a, 0) for i in range(10): self.assertGreater(t.time(), a) a = t.time() t.reset() b = t.time() self.assertGreaterEqual(b, 0) self.assertLess(b, a)
def test_notebook(path): """ Tests a notebook in a subprocess, exists if it doesn't finish. """ import nbconvert import pints b = pints.Timer() print('Running ' + path + ' ... ', end='') sys.stdout.flush() # Load notebook, convert to python e = nbconvert.exporters.PythonExporter() code, __ = e.from_filename(path) # Remove coding statement, if present code = '\n'.join([x for x in code.splitlines() if x[:9] != '# coding']) # Tell matplotlib not to produce any figures env = os.environ.copy() env['MPLBACKEND'] = 'Template' # Run in subprocess cmd = [sys.executable, '-c', code] try: p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) stdout, stderr = p.communicate() # TODO: Use p.communicate(timeout=3600) if Python3 only if p.returncode != 0: # Show failing code, output and errors before returning print('ERROR') print('-- script ' + '-' * (79 - 10)) for i, line in enumerate(code.splitlines()): j = str(1 + i) print(j + ' ' * (5 - len(j)) + line) print('-- stdout ' + '-' * (79 - 10)) print(stdout) print('-- stderr ' + '-' * (79 - 10)) print(stderr) print('-' * 79) return False except KeyboardInterrupt: p.terminate() print('ABORTED') sys.exit(1) # Sucessfully run print('ok (' + b.format() + ')') return True
def test_format(self): """ Test the format() method. """ t = pints.Timer() self.assertEqual(t.format(1e-3), '0.001 seconds') self.assertEqual(t.format(2), '2 seconds') self.assertEqual(t.format(2.5), '2.5 seconds') self.assertEqual(t.format(12.5), '12.5 seconds') self.assertEqual(t.format(59.41), '59.41 seconds') self.assertEqual(t.format(60.2), '1 minute, 0 seconds') self.assertEqual(t.format(61), '1 minute, 1 second') self.assertEqual(t.format(121), '2 minutes, 1 second') self.assertEqual(t.format(604800), '1 week, 0 days, 0 hours, 0 minutes, 0 seconds') self.assertEqual(t.format(2 * 604800 + 3 * 3600 + 60 + 4), '2 weeks, 0 days, 3 hours, 1 minute, 4 seconds')
def test_quick_run(self): # Test a single run. sampler = pints.NestedController(self.log_likelihood, self.log_prior) sampler.set_n_posterior_samples(10) sampler.set_iterations(50) sampler.set_log_to_screen(False) # Time before run is None self.assertIsNone(sampler.time()) t = pints.Timer() samples = sampler.run() t_upper = t.time() # Check output: Note n returned samples = n posterior samples self.assertEqual(samples.shape, (10, 2)) # Time after run is greater than zero self.assertIsInstance(sampler.time(), float) self.assertGreater(sampler.time(), 0) self.assertGreater(t_upper, sampler.time())
def run(self): """ Runs the optimisation, returns a tuple ``(xbest, fbest)``. """ # Check stopping criteria has_stopping_criterion = False has_stopping_criterion |= (self._max_iterations is not None) has_stopping_criterion |= (self._max_unchanged_iterations is not None) has_stopping_criterion |= (self._threshold is not None) if not has_stopping_criterion: raise ValueError('At least one stopping criterion must be set.') # Iterations and function evaluations iteration = 0 evaluations = 0 # Unchanged iterations count (used for stopping or just for # information) unchanged_iterations = 0 # Create evaluator object if self._parallel: # Get number of workers n_workers = self._n_workers # For population based optimisers, don't use more workers than # particles! if isinstance(self._optimiser, PopulationBasedOptimiser): n_workers = min(n_workers, self._optimiser.population_size()) evaluator = pints.ParallelEvaluator(self._function, n_workers=n_workers) else: evaluator = pints.SequentialEvaluator(self._function) # Keep track of best position and score fbest = float('inf') # Internally we always minimise! Keep a 2nd value to show the user fbest_user = fbest if self._minimising else -fbest # Set up progress reporting next_message = 0 # Start logging logging = self._log_to_screen or self._log_filename if logging: if self._log_to_screen: # Show direction if self._minimising: print('Minimising error measure') else: print('Maximising LogPDF') # Show method print('Using ' + str(self._optimiser.name())) # Show parallelisation if self._parallel: print('Running in parallel with ' + str(n_workers) + ' worker processes.') else: print('Running in sequential mode.') # Show population size pop_size = 1 if isinstance(self._optimiser, PopulationBasedOptimiser): pop_size = self._optimiser.population_size() if self._log_to_screen: print('Population size: ' + str(pop_size)) # Set up logger logger = pints.Logger() if not self._log_to_screen: logger.set_stream(None) if self._log_filename: logger.set_filename(self._log_filename, csv=self._log_csv) # Add fields to log max_iter_guess = max(self._max_iterations or 0, 10000) max_eval_guess = max_iter_guess * pop_size logger.add_counter('Iter.', max_value=max_iter_guess) logger.add_counter('Eval.', max_value=max_eval_guess) logger.add_float('Best') self._optimiser._log_init(logger) logger.add_time('Time m:s') # Start searching timer = pints.Timer() running = True try: while running: # Get points xs = self._optimiser.ask() # Calculate scores fs = evaluator.evaluate(xs) # Perform iteration self._optimiser.tell(fs) # Check if new best found fnew = self._optimiser.fbest() if fnew < fbest: # Check if this counts as a significant change if np.abs(fnew - fbest) < self._min_significant_change: unchanged_iterations += 1 else: unchanged_iterations = 0 # Update best fbest = fnew # Update user value of fbest fbest_user = fbest if self._minimising else -fbest else: unchanged_iterations += 1 # Update evaluation count evaluations += len(fs) # Show progress if logging and iteration >= next_message: # Log state logger.log(iteration, evaluations, fbest_user) self._optimiser._log_write(logger) logger.log(timer.time()) # Choose next logging point if iteration < self._message_warm_up: next_message = iteration + 1 else: next_message = self._message_interval * ( 1 + iteration // self._message_interval) # Update iteration count iteration += 1 # # Check stopping criteria # # Maximum number of iterations if (self._max_iterations is not None and iteration >= self._max_iterations): running = False halt_message = ('Halting: Maximum number of iterations (' + str(iteration) + ') reached.') # Maximum number of iterations without significant change halt = (self._max_unchanged_iterations is not None and unchanged_iterations >= self._max_unchanged_iterations) if halt: running = False halt_message = ('Halting: No significant change for ' + str(unchanged_iterations) + ' iterations.') # Threshold value if self._threshold is not None and fbest < self._threshold: running = False halt_message = ('Halting: Objective function crossed' ' threshold: ' + str(self._threshold) + '.') # Error in optimiser error = self._optimiser.stop() if error: # pragma: no cover running = False halt_message = ('Halting: ' + str(error)) except (Exception, SystemExit, KeyboardInterrupt): # pragma: no cover # Unexpected end! # Show last result and exit print('\n' + '-' * 40) print('Unexpected termination.') print('Current best score: ' + str(fbest)) print('Current best position:') for p in self._optimiser.xbest(): print(pints.strfloat(p)) print('-' * 40) raise time_taken = timer.time() # Log final values and show halt message if logging: logger.log(iteration, evaluations, fbest_user) self._optimiser._log_write(logger) logger.log(time_taken) if self._log_to_screen: print(halt_message) # Save post-run statistics self._evaluations = evaluations self._iterations = iteration self._time = time_taken # Return best position and score return self._optimiser.xbest(), fbest_user
def run(self): """ Runs the MCMC sampler(s) and returns the result. By default, this method returns an array of shape ``(n_chains, n_iterations, n_parameters)``. If storing chains to memory has been disabled with :meth:`set_chain_storage`, then ``None`` is returned instead. """ # Check stopping criteria has_stopping_criterion = False has_stopping_criterion |= (self._max_iterations is not None) if not has_stopping_criterion: raise ValueError('At least one stopping criterion must be set.') # Iteration and evaluation counting iteration = 0 n_evaluations = 0 # Choose method to evaluate f = self._log_pdf if self._needs_sensitivities: f = f.evaluateS1 # Create evaluator object if self._parallel: # Use at most n_workers workers n_workers = min(self._n_workers, self._n_chains) evaluator = pints.ParallelEvaluator(f, n_workers=n_workers) else: evaluator = pints.SequentialEvaluator(f) # Initial phase if self._needs_initial_phase: for sampler in self._samplers: sampler.set_initial_phase(True) # Storing evaluations to memory or disk prior = None store_evaluations = \ self._evaluations_in_memory or self._evaluation_files if store_evaluations: # Bayesian inference on a log-posterior? Then separate out the # prior so we can calculate the loglikelihood if isinstance(self._log_pdf, pints.LogPosterior): prior = self._log_pdf.log_prior() # Store last accepted logpdf, per chain current_logpdf = np.zeros(self._n_chains) current_prior = np.zeros(self._n_chains) # Write chains to disk chain_loggers = [] if self._chain_files: for filename in self._chain_files: cl = pints.Logger() cl.set_stream(None) cl.set_filename(filename, True) for k in range(self._n_parameters): cl.add_float('p' + str(k)) chain_loggers.append(cl) # Write evaluations to disk eval_loggers = [] if self._evaluation_files: for filename in self._evaluation_files: cl = pints.Logger() cl.set_stream(None) cl.set_filename(filename, True) if prior: # Logposterior in first column, to be consistent with the # non-bayesian case cl.add_float('logposterior') cl.add_float('loglikelihood') cl.add_float('logprior') else: cl.add_float('logpdf') eval_loggers.append(cl) # Set up progress reporting next_message = 0 # Start logging logging = self._log_to_screen or self._log_filename if logging: if self._log_to_screen: print('Using ' + str(self._samplers[0].name())) print('Generating ' + str(self._n_chains) + ' chains.') if self._parallel: print('Running in parallel with ' + str(n_workers) + ' worker processess.') else: print('Running in sequential mode.') if self._chain_files: print('Writing chains to ' + self._chain_files[0] + ' etc.') if self._evaluation_files: print('Writing evaluations to ' + self._evaluation_files[0] + ' etc.') # Set up logger logger = pints.Logger() if not self._log_to_screen: logger.set_stream(None) if self._log_filename: logger.set_filename(self._log_filename, csv=self._log_csv) # Add fields to log max_iter_guess = max(self._max_iterations or 0, 10000) max_eval_guess = max_iter_guess * self._n_chains logger.add_counter('Iter.', max_value=max_iter_guess) logger.add_counter('Eval.', max_value=max_eval_guess) for sampler in self._samplers: sampler._log_init(logger) logger.add_time('Time m:s') # Pre-allocate arrays for chain storage if self._chains_in_memory: # Store full chains samples = np.zeros( (self._n_chains, self._max_iterations, self._n_parameters)) else: # Store only the current iteration samples = np.zeros((self._n_chains, self._n_parameters)) # Pre-allocate arrays for evaluation storage if self._evaluations_in_memory: if prior: # Store posterior, likelihood, prior evaluations = np.zeros( (self._n_chains, self._max_iterations, 3)) else: # Store pdf evaluations = np.zeros((self._n_chains, self._max_iterations)) # Some samplers need intermediate steps, where None is returned instead # of a sample. Samplers can run asynchronously, so that one returns # None while another returns a sample. # To deal with this, we maintain a list of 'active' samplers that have # not reach `max_iterations` yet, and store the number of samples we # have in each chain. if self._single_chain: active = list(range(self._n_chains)) n_samples = [0] * self._n_chains # Start sampling timer = pints.Timer() running = True while running: # Initial phase # Note: self._initial_phase_iterations is None when no initial # phase is needed if iteration == self._initial_phase_iterations: for sampler in self._samplers: sampler.set_initial_phase(False) if self._log_to_screen: print('Initial phase completed.') # Get points if self._single_chain: xs = [self._samplers[i].ask() for i in active] else: xs = self._samplers[0].ask() # Calculate logpdfs fxs = evaluator.evaluate(xs) # Update evaluation count n_evaluations += len(fxs) # Update chains if self._single_chain: # Single chain # Check and update the individual chains xs_iterator = iter(xs) fxs_iterator = iter(fxs) for i in list(active): # new list: active may be modified x = next(xs_iterator) fx = next(fxs_iterator) y = self._samplers[i].tell(fx) if y is not None: # Store sample in memory if self._chains_in_memory: samples[i][n_samples[i]] = y else: samples[i] = y # Update current evaluations if store_evaluations: # Check if accepted, if so, update log_pdf and # prior to be logged accepted = np.all(y == x) if accepted: current_logpdf[i] = fx if prior is not None: current_prior[i] = prior(y) # Calculate evaluations to log e = current_logpdf[i] if prior is not None: e = [ e, current_logpdf[i] - current_prior[i], current_prior[i] ] # Store evaluations in memory if self._evaluations_in_memory: evaluations[i][n_samples[i]] = e # Write evaluations to disk if self._evaluation_files: if prior is None: eval_loggers[i].log(e) else: eval_loggers[i].log(*e) # Stop adding samples if maximum number reached n_samples[i] += 1 if n_samples[i] == self._max_iterations: active.remove(i) # This is an intermediate step until the slowest sampler has # produced a new sample since the last `iteration`. intermediate_step = min(n_samples) <= iteration else: # Multi-chain methods # Get all chains samples at once ys = self._samplers[0].tell(fxs) intermediate_step = ys is None if not intermediate_step: # Store samples in memory if self._chains_in_memory: samples[:, iteration] = ys else: samples = ys # Update current evaluations if store_evaluations: es = [] for i, y in enumerate(ys): # Check if accepted, if so, update log_pdf and # prior to be logged accepted = np.all(xs[i] == y) if accepted: current_logpdf[i] = fxs[i] if prior is not None: current_prior[i] = prior(ys[i]) # Calculate evaluations to log e = current_logpdf[i] if prior is not None: e = [ e, current_logpdf[i] - current_prior[i], current_prior[i] ] es.append(e) # Write evaluations to memory if self._evaluations_in_memory: for i, e in enumerate(es): evaluations[i, iteration] = e # Write evaluations to disk if self._evaluation_files: if prior is None: for i, eval_logger in enumerate(eval_loggers): eval_logger.log(es[i]) else: for i, eval_logger in enumerate(eval_loggers): eval_logger.log(*es[i]) # If no new samples were added, then no MCMC iteration was # performed, and so the iteration count shouldn't be updated, # logging shouldn't be triggered, and stopping criteria shouldn't # be checked if intermediate_step: continue # Write samples to disk if self._chains_in_memory: for i, chain_logger in enumerate(chain_loggers): chain_logger.log(*samples[i][iteration]) else: for i, chain_logger in enumerate(chain_loggers): chain_logger.log(*samples[i]) # Show progress if logging and iteration >= next_message: # Log state logger.log(iteration, n_evaluations) for sampler in self._samplers: sampler._log_write(logger) logger.log(timer.time()) # Choose next logging point if iteration < self._message_warm_up: next_message = iteration + 1 else: next_message = self._message_interval * ( 1 + iteration // self._message_interval) # Update iteration count iteration += 1 # Check requested number of samples if (self._max_iterations is not None and iteration >= self._max_iterations): running = False halt_message = ('Halting: Maximum number of iterations (' + str(iteration) + ') reached.') # Log final state and show halt message if logging: logger.log(iteration, n_evaluations) for sampler in self._samplers: sampler._log_write(logger) logger.log(timer.time()) if self._log_to_screen: print(halt_message) # Store generated chains in memory if self._chains_in_memory: self._samples = samples # Store evaluations in memory if self._evaluations_in_memory: self._evaluations = evaluations # Return generated chains return samples if self._chains_in_memory else None
def run(self): """ Runs the optimisation, returns a tuple ``(x_best, f_best)``. An optional ``callback`` function can be passed in that will be called at the end of every iteration. The callback should take the arguments ``(iteration, optimiser)``, where ``iteration`` is the iteration count (an integer) and ``optimiser`` is the optimiser object. """ # Can only run once for each controller instance if self._has_run: raise RuntimeError("Controller is valid for single use only") self._has_run = True # Check stopping criteria has_stopping_criterion = False has_stopping_criterion |= (self._max_iterations is not None) has_stopping_criterion |= (self._unchanged_max_iterations is not None) has_stopping_criterion |= (self._max_evaluations is not None) has_stopping_criterion |= (self._threshold is not None) if not has_stopping_criterion: raise ValueError('At least one stopping criterion must be set.') # Iterations and function evaluations iteration = 0 evaluations = 0 # Unchanged iterations count (used for stopping or just for # information) unchanged_iterations = 0 # Choose method to evaluate f = self._function if self._needs_sensitivities: f = f.evaluateS1 # Create evaluator object if self._parallel: # Get number of workers n_workers = self._n_workers # For population based optimisers, don't use more workers than # particles! if isinstance(self._optimiser, PopulationBasedOptimiser): n_workers = min(n_workers, self._optimiser.population_size()) evaluator = pints.ParallelEvaluator(f, n_workers=n_workers) else: evaluator = pints.SequentialEvaluator(f) # Keep track of current best and best-guess scores. fb = fg = float('inf') # Internally we always minimise! Keep a 2nd value to show the user. fb_user, fg_user = (fb, fg) if self._minimising else (-fb, -fg) # Keep track of the last significant change f_sig = float('inf') # Set up progress reporting next_message = 0 # Start logging logging = self._log_to_screen or self._log_filename if logging: if self._log_to_screen: # Show direction if self._minimising: print('Minimising error measure') else: print('Maximising LogPDF') # Show method print('Using ' + str(self._optimiser.name())) # Show parallelisation if self._parallel: print('Running in parallel with ' + str(n_workers) + ' worker processes.') else: print('Running in sequential mode.') # Show population size pop_size = 1 if isinstance(self._optimiser, PopulationBasedOptimiser): pop_size = self._optimiser.population_size() if self._log_to_screen: print('Population size: ' + str(pop_size)) # Set up logger logger = pints.Logger() if not self._log_to_screen: logger.set_stream(None) if self._log_filename: logger.set_filename(self._log_filename, csv=self._log_csv) # Add fields to log max_iter_guess = max(self._max_iterations or 0, 10000) max_eval_guess = max( self._max_evaluations or 0, max_iter_guess * pop_size) logger.add_counter('Iter.', max_value=max_iter_guess) logger.add_counter('Eval.', max_value=max_eval_guess) logger.add_float('Best') logger.add_float('Current') self._optimiser._log_init(logger) logger.add_time('Time m:s') # Start searching timer = pints.Timer() running = True try: while running: # Get points xs = self._optimiser.ask() # Calculate scores fs = evaluator.evaluate(xs) # Perform iteration self._optimiser.tell(fs) # Update current scores fb = self._optimiser.f_best() fg = self._optimiser.f_guessed() fb_user, fg_user = (fb, fg) if self._minimising else (-fb, -fg) # Check for significant changes f_new = fg if self._use_f_guessed else fb if np.abs(f_new - f_sig) >= self._unchanged_threshold: unchanged_iterations = 0 f_sig = f_new else: unchanged_iterations += 1 # Update evaluation count evaluations += len(fs) # Show progress if logging and iteration >= next_message: # Log state logger.log(iteration, evaluations, fb_user, fg_user) self._optimiser._log_write(logger) logger.log(timer.time()) # Choose next logging point if iteration < self._message_warm_up: next_message = iteration + 1 else: next_message = self._message_interval * ( 1 + iteration // self._message_interval) # Update iteration count iteration += 1 # # Check stopping criteria # # Maximum number of iterations if (self._max_iterations is not None and iteration >= self._max_iterations): running = False halt_message = ('Maximum number of iterations (' + str(iteration) + ') reached.') # Maximum number of iterations without significant change halt = (self._unchanged_max_iterations is not None and unchanged_iterations >= self._unchanged_max_iterations) if running and halt: running = False halt_message = ('No significant change for ' + str(unchanged_iterations) + ' iterations.') # Maximum number of evaluations if (self._max_evaluations is not None and evaluations >= self._max_evaluations): running = False halt_message = ( 'Maximum number of evaluations (' + str(self._max_evaluations) + ') reached.') # Threshold value halt = (self._threshold is not None and f_new < self._threshold) if running and halt: running = False halt_message = ('Objective function crossed threshold: ' + str(self._threshold) + '.') # Error in optimiser error = self._optimiser.stop() if error: # pragma: no cover running = False halt_message = str(error) elif self._callback is not None: self._callback(iteration - 1, self._optimiser) except (Exception, SystemExit, KeyboardInterrupt): # pragma: no cover # Unexpected end! # Show last result and exit print('\n' + '-' * 40) print('Unexpected termination.') print('Current score: ' + str(fg_user)) print('Current position:') # Show current parameters x_user = self._optimiser.x_guessed() if self._transformation is not None: x_user = self._transformation.to_model(x_user) for p in x_user: print(pints.strfloat(p)) print('-' * 40) raise # Stop timer self._time = timer.time() # Log final values and show halt message if logging: if iteration - 1 < next_message: logger.log(iteration, evaluations, fb_user, fg_user) self._optimiser._log_write(logger) logger.log(self._time) if self._log_to_screen: print('Halting: ' + halt_message) # Save post-run statistics self._evaluations = evaluations self._iterations = iteration # Get best parameters if self._use_f_guessed: x = self._optimiser.x_guessed() f = self._optimiser.f_guessed() else: x = self._optimiser.x_best() f = self._optimiser.f_best() # Inverse transform search parameters if self._transformation is not None: x = self._transformation.to_model(x) # Return best position and score return x, f if self._minimising else -f
def run(self): """ Runs the MCMC sampler(s) and returns a number of markov chains, each representing the distribution of the given log-pdf. """ # Check stopping criteria has_stopping_criterion = False has_stopping_criterion |= (self._max_iterations is not None) if not has_stopping_criterion: raise ValueError('At least one stopping criterion must be set.') # Iteration and evaluation counting iteration = 0 evaluations = 0 # Choose method to evaluate f = self._log_pdf if self._needs_sensitivities: f = f.evaluateS1 # Create evaluator object if self._parallel: # Use at most n_workers workers n_workers = min(self._n_workers, self._chains) evaluator = pints.ParallelEvaluator(f, n_workers=n_workers) else: evaluator = pints.SequentialEvaluator(f) # Initial phase if self._needs_initial_phase: for sampler in self._samplers: sampler.set_initial_phase(True) # Write chains to disk chain_loggers = [] if self._chain_files: for filename in self._chain_files: cl = pints.Logger() cl.set_stream(None) cl.set_filename(filename, True) for k in range(self._n_parameters): cl.add_float('p' + str(k)) chain_loggers.append(cl) # Write evaluations to disk eval_loggers = [] if self._evaluation_files: # Bayesian inference on a log-posterior? Then separate out the # prior so we can calculate the loglikelihood prior = None if isinstance(self._log_pdf, pints.LogPosterior): prior = self._log_pdf.log_prior() # Set up loggers for filename in self._evaluation_files: cl = pints.Logger() cl.set_stream(None) cl.set_filename(filename, True) if prior: # Logposterior in first column, to be consistent with the # non-bayesian case cl.add_float('logposterior') cl.add_float('loglikelihood') cl.add_float('logprior') else: cl.add_float('logpdf') eval_loggers.append(cl) # Store last accepted logpdf, per chain current_logpdf = np.zeros(self._chains) current_prior = np.zeros(self._chains) # Set up progress reporting next_message = 0 # Start logging logging = self._log_to_screen or self._log_filename if logging: if self._log_to_screen: print('Using ' + str(self._samplers[0].name())) print('Generating ' + str(self._chains) + ' chains.') if self._parallel: print('Running in parallel with ' + str(n_workers) + ' worker processess.') else: print('Running in sequential mode.') if self._chain_files: print('Writing chains to ' + self._chain_files[0] + ' etc.') if self._evaluation_files: print('Writing evaluations to ' + self._evaluation_files[0] + ' etc.') # Set up logger logger = pints.Logger() if not self._log_to_screen: logger.set_stream(None) if self._log_filename: logger.set_filename(self._log_filename, csv=self._log_csv) # Add fields to log max_iter_guess = max(self._max_iterations or 0, 10000) max_eval_guess = max_iter_guess * self._chains logger.add_counter('Iter.', max_value=max_iter_guess) logger.add_counter('Eval.', max_value=max_eval_guess) for sampler in self._samplers: sampler._log_init(logger) logger.add_time('Time m:s') # Create chains # TODO Pre-allocate? chains = [] # Start sampling timer = pints.Timer() running = True while running: # Initial phase # Note: self._initial_phase_iterations is None when no initial # phase is needed if iteration == self._initial_phase_iterations: for sampler in self._samplers: sampler.set_initial_phase(False) if self._log_to_screen: print('Initial phase completed.') # Get points if self._single_chain: xs = [sampler.ask() for sampler in self._samplers] else: xs = self._samplers[0].ask() # Calculate logpdfs fxs = evaluator.evaluate(xs) # Update evaluation count evaluations += len(fxs) # Update chains intermediate_step = False if self._single_chain: samples = np.array( [s.tell(fxs[i]) for i, s in enumerate(self._samplers)]) none_found = [x is None for x in samples] if any(none_found): assert (all(none_found)) # Can't mix None w. samples intermediate_step = True else: samples = self._samplers[0].tell(fxs) intermediate_step = samples is None # If no new samples were added, then no MCMC iteration was # performed, and so the iteration count shouldn't be updated, # logging shouldn't be triggered, and stopping criteria shouldn't # be checked if intermediate_step: continue # Add new samples to the chains chains.append(samples) # Write samples to disk for k, chain_logger in enumerate(chain_loggers): chain_logger.log(*samples[k]) # Write evaluations to disk if self._evaluation_files: for k, eval_logger in enumerate(eval_loggers): if np.all(xs[k] == samples[k]): current_logpdf[k] = fxs[k] if prior is not None: current_prior[k] = prior(xs[k]) eval_logger.log(current_logpdf[k]) if prior is not None: eval_logger.log(current_logpdf[k] - current_prior[k]) eval_logger.log(current_prior[k]) # Show progress if logging and iteration >= next_message: # Log state logger.log(iteration, evaluations) for sampler in self._samplers: sampler._log_write(logger) logger.log(timer.time()) # Choose next logging point if iteration < self._message_warm_up: next_message = iteration + 1 else: next_message = self._message_interval * ( 1 + iteration // self._message_interval) # Update iteration count iteration += 1 # # Check stopping criteria # # Maximum number of iterations if (self._max_iterations is not None and iteration >= self._max_iterations): running = False halt_message = ('Halting: Maximum number of iterations (' + str(iteration) + ') reached.') # TODO Add more stopping criteria # Log final state and show halt message if logging: logger.log(iteration, evaluations) for sampler in self._samplers: sampler._log_write(logger) logger.log(timer.time()) if self._log_to_screen: print(halt_message) # Swap axes in chains, to get indices # [chain, iteration, parameter] chains = np.array(chains) chains = chains.swapaxes(0, 1) # Return generated chains return chains
def run(self): """ See :meth:`pints.MCMC.run()`. """ # Check if settings are sensible max_post = 0.25 * (self._iterations + self._active_points) if self._posterior_samples > max_post: raise ValueError( 'Number of posterior samples must not exceed 0.25 times (the' ' number of iterations + the number of active points).') # Set up progress reporting next_message = 0 message_warm_up = 3 message_interval = 20 # Start logging logging = self._log_to_screen or self._log_filename if logging: # Create timer timer = pints.Timer() if self._log_to_screen: # Show current settings print('Running nested rejection sampling') print('Number of active points: ' + str(self._active_points)) print('Total number of iterations: ' + str(self._iterations)) print('Total number of posterior samples: ' + str( self._posterior_samples)) # Set up logger logger = pints.Logger() if not self._log_to_screen: logger.set_stream(None) if self._log_filename: logger.set_filename(self._log_filename, csv=self._log_csv) # Add fields to log logger.add_counter('Iter.', max_value=self._iterations) logger.add_counter('Eval.', max_value=self._iterations * 10) # TODO: Add other informative fields ? logger.add_time('Time m:s') # Problem dimension d = self._n_parameters # Generate initial random points by sampling from the prior m_active = np.zeros((self._active_points, d + 1)) m_initial = self._log_prior.sample(self._active_points) for i in range(0, self._active_points): # Calculate likelihood m_active[i, d] = self._log_likelihood(m_initial[i, :]) self._n_evals += 1 # Show progress if logging and i >= next_message: # Log state logger.log(0, self._n_evals, timer.time()) # Choose next logging point if i > message_warm_up: next_message = message_interval * ( 1 + i // message_interval) m_active[:, :-1] = m_initial # store all inactive points, along with their respective # log-likelihoods (hence, d+1) m_inactive = np.zeros((self._iterations, d + 1)) # store weights w = np.zeros(self._active_points + self._iterations) # store X values (defined in [1]) X = np.zeros(self._iterations + 1) X[0] = 1 # log marginal likelihood holder v_log_Z = np.zeros(self._iterations + 1) # Run i_message = self._active_points - 1 for i in range(0, self._iterations): a_running_log_likelihood = np.min(m_active[:, d]) a_min_index = np.argmin(m_active[:, d]) X[i + 1] = np.exp(-(i + 1) / self._active_points) w[i] = X[i] - X[i + 1] v_log_Z[i] = a_running_log_likelihood m_inactive[i, :] = m_active[a_min_index, :] # Independently samples params from the prior until # log_likelihood(params) > threshold. # Note a_running_log_likelihood can be -inf, so while is never run proposed = self._log_prior.sample()[0] log_likelihood = self._log_likelihood(proposed) self._n_evals += 1 while log_likelihood < a_running_log_likelihood: proposed = self._log_prior.sample()[0] log_likelihood = self._log_likelihood(proposed) self._n_evals += 1 m_active[a_min_index, :] = np.concatenate( (proposed, np.array([log_likelihood]))) # Show progress if logging: i_message += 1 if i_message >= next_message: # Log state logger.log(i_message, self._n_evals, timer.time()) # Choose next logging point if i_message > message_warm_up: next_message = message_interval * ( 1 + i_message // message_interval) v_log_Z[self._iterations] = logsumexp(m_active[:, d]) w[self._iterations:] = float(X[self._iterations]) / float( self._active_points) m_samples_all = np.vstack((m_inactive, m_active)) log_Z = logsumexp(v_log_Z, b=w[0:(self._iterations + 1)]) vP = np.exp(m_samples_all[:, d] - log_Z) * w m_theta = m_samples_all[:, :-1] vIndex = np.random.choice( range(0, self._iterations + self._active_points), self._posterior_samples, p=vP) m_posterior_samples = m_theta[vIndex, :] return m_posterior_samples, log_Z
def run(self): """ See :meth:`pints.MCMC.run()`. """ # Reset total number of log_likelihood evaluations self._n_evals = 0 # Check if settings make sense max_post = 0.25 * (self._iterations + self._active_points) if self._posterior_samples > max_post: raise ValueError( 'Number of posterior samples must not exceed 0.25 times (the' ' number of iterations + the number of active points).') if self._rejection_samples > self._iterations: raise ValueError( 'Number of rejection samples must not exceed number of' ' iterations.') # Set up progress reporting next_message = 0 message_warm_up = 3 message_interval = 20 # Start logging logging = self._log_to_screen or self._log_filename if logging: # Create timer timer = pints.Timer() if self._log_to_screen: # Show current settings print('Running nested rejection sampling') print('Number of active points: ' + str(self._active_points)) print('Total number of iterations: ' + str(self._iterations)) print('Enlargement factor: ' + str(self._enlargement_factor)) print('Total number of posterior samples: ' + str( self._posterior_samples)) # Set up logger logger = pints.Logger() if not self._log_to_screen: logger.set_stream(None) if self._log_filename: logger.set_filename(self._log_filename, csv=self._log_csv) # Add fields to log logger.add_counter('Iter.', max_value=self._iterations) logger.add_counter('Eval.', max_value=self._iterations * 10) # TODO: Add other informative fields ? logger.add_time('Time m:s') # Problem dimension d = self._n_parameters # Generate initial random points by sampling from the prior m_active = np.zeros((self._active_points, d + 1)) m_initial = self._log_prior.sample(self._active_points) for i in range(0, self._active_points): # Evaluate log likelihood m_active[i, d] = self._log_likelihood(m_initial[i, :]) self._n_evals += 1 # Show progress if logging and i >= next_message: # Log state logger.log(0, self._n_evals, timer.time()) # Choose next logging point if i > message_warm_up: next_message = message_interval * ( 1 + i // message_interval) m_active[:, :-1] = m_initial # store all inactive points, along with their respective # log-likelihoods (hence, d+1) m_inactive = np.zeros((self._iterations, d + 1)) # store weights w = np.zeros(self._active_points + self._iterations) # store X values (defined in [1]) X = np.zeros(self._iterations + 1) X[0] = 1 # log marginal likelihood holder v_log_Z = np.zeros(self._iterations + 1) # Run i_message = self._active_points - 1 for i in range(0, self._iterations): a_running_log_likelihood = np.min(m_active[:, d]) a_min_index = np.argmin(m_active[:, d]) X[i + 1] = np.exp(-(i + 1.0) / self._active_points) w[i] = X[i] - X[i + 1] v_log_Z[i] = a_running_log_likelihood m_inactive[i, :] = m_active[a_min_index, :] if (i + 1) % self._rejection_samples == 0: A, centroid = self._minimum_volume_ellipsoid(m_active[:, :d]) if i > self._rejection_samples: if ((i + 1 - self._rejection_samples) % self._ellipsoid_update_gap == 0): A, centroid = self._minimum_volume_ellipsoid( m_active[:, :d]) if i < self._rejection_samples: # Start off with rejection sampling, while this is still very # efficient. m_active[a_min_index, :] = self._reject_sample_prior( a_running_log_likelihood) else: # After a number of samples, switch to ellipsoid sampling. m_active[a_min_index, :] = \ self._reject_ellipsoid_sample_faster( a_running_log_likelihood, m_active[:, :d], self._enlargement_factor, A, centroid) # Show progress if logging: i_message += 1 if i_message >= next_message: # Log state logger.log(i_message, self._n_evals, timer.time()) # Choose next logging point if i_message > message_warm_up: next_message = message_interval * ( 1 + i_message // message_interval) v_log_Z[self._iterations] = logsumexp(m_active[:, d]) w[self._iterations:] = \ float(X[self._iterations]) / float(self._active_points) m_samples_all = np.vstack((m_inactive, m_active)) logZ = logsumexp(v_log_Z, b=w[0:(self._iterations + 1)]) vP = np.exp(m_samples_all[:, d] - logZ) * w mTheta = m_samples_all[:, :-1] vIndex = np.random.choice( range(0, self._iterations + self._active_points), self._posterior_samples, p=vP) m_posterior_samples = mTheta[vIndex, :] return m_posterior_samples, logZ
def run(self): """ Runs the nested sampling routine and returns a tuple of the posterior samples and an estimate of the marginal likelihood. """ # Choose method to evaluate f = self._initialise_callable() # Set parallel self._evaluator = self._initialise_evaluator(f) # Set number of active points self._n_active_points = self._sampler.n_active_points() # Start timing self._timer = pints.Timer() # Set up progress reporting self._next_message = 0 self._message_warm_up = 0 self._message_interval = 20 self._initialise_logger() d = self._n_parameters v_fx, m_initial = self._initial_points() self._sampler._initialise_active_points(m_initial, v_fx) # store all inactive points, along with their respective # log-likelihoods (hence, d+1) self._m_inactive = np.zeros((self._iterations, d + 1)) # store weights self._w = np.zeros(self._n_active_points + self._iterations) # store X values (defined in [1]) self._X = np.zeros(self._iterations + 1) self._X[0] = 1 # log marginal likelihood holder self._v_log_Z = np.zeros(self._iterations + 1) # Run! self._X[0] = 1.0 self._i_message = 0 i_winners = 0 m_previous_winners = [] for i in range(0, self._iterations): i_iter_complete = 0 self._i = i a_min_index = self._sampler.min_index() self._X[i + 1] = np.exp(-(i + 1) / self._n_active_points) if i > 0: self._w[i] = 0.5 * (self._X[i - 1] - self._X[i + 1]) else: self._w[i] = self._X[i] - self._X[i + 1] self._v_log_Z[i] = self._sampler.running_log_likelihood() self._m_inactive[i, :] = self._sampler._m_active[a_min_index, :] # check whether previous winners exceed threshold if i_winners > 0: m_previous_winners = m_previous_winners[( m_previous_winners[:, self._n_parameters] > self._sampler. running_log_likelihood()), :] if m_previous_winners.shape[0] > 0: index = np.random.choice(m_previous_winners.shape[0], 1, replace=False) proposed = m_previous_winners[index, :self._n_parameters] fx_temp = m_previous_winners[index, self._n_parameters] m_previous_winners = np.delete(m_previous_winners, index, 0) self._sampler._m_active[self._sampler._min_index, :] = ( np.concatenate((proposed[0], fx_temp))) self._sampler._min_index = np.argmin( self._sampler._m_active[:, self._n_parameters]) self._sampler._set_running_log_likelihood( np.min(self._sampler._m_active[:, self._n_parameters])) self._sampler._accept_count += 1 i_iter_complete = 1 if i_iter_complete == 0: # Propose new samples proposed = self._sampler.ask(self._n_workers) # Evaluate their fit if self._n_workers > 1: log_likelihood = self._evaluator.evaluate(proposed) else: log_likelihood = self._evaluator.evaluate([proposed])[0] sample, winners = self._sampler.tell(log_likelihood) while sample is None: proposed = self._sampler.ask(self._n_workers) if self._n_workers > 1: log_likelihood = ( # pragma: no cover self._evaluator.evaluate(proposed)) else: log_likelihood = self._evaluator.evaluate([proposed ])[0] sample, winners = self._sampler.tell(log_likelihood) if winners.size > 0: if i_winners == 0: m_previous_winners = winners i_winners = 1 else: m_previous_winners = [m_previous_winners, winners] m_previous_winners = np.concatenate(m_previous_winners) # Check whether within convergence threshold if i > 2: self._diff_marginal_likelihood(i, d) if (np.abs(self._diff) < self._marginal_log_likelihood_threshold): if self._log_to_screen: print( # pragma: no cover 'Convergence obtained with Delta_z = ' + str(self._diff)) # shorten arrays according to current iteration self._iterations = i self._v_log_Z = self._v_log_Z[0:(self._iterations + 1)] self._w = self._w[0:(self._n_active_points + self._iterations)] self._X = self._X[0:(self._iterations + 1)] self._m_inactive = self._m_inactive[0:self._iterations, :] break # Show progress self._update_logger() # Calculate log_evidence and uncertainty self._log_Z = self.marginal_log_likelihood() self._log_Z_sd = self.marginal_log_likelihood_standard_deviation() # Draw samples from posterior n = self._posterior_samples self._m_posterior_samples = self.sample_from_posterior(n) # Stop timer self._time = self._timer.time() return self._m_posterior_samples
def run(self): """ Runs the ABC sampler. """ if self._max_iterations is None: raise ValueError("At least one stopping criterion must be set.") # Iteration and evaluation counting iteration = 0 evaluations = 0 accepted_count = 0 # Choose method to evaluate f = self._error_measure # Create evaluator if self._parallel: n_workers = self._n_workers evaluator = pints.ParallelEvaluator(f, n_workers=n_workers) else: evaluator = pints.SequentialEvaluator(f) # Set up progress reporting next_message = 0 # Start logging logging = self._log_to_screen or self._log_filename if logging: if self._log_to_screen: print('Using ' + str(self._sampler.name())) if self._parallel: print('Running in parallel with ' + str(n_workers) + ' worker processess.') else: print('Running in sequential mode.') # Set up logger logger = pints.Logger() if not self._log_to_screen: logger.set_stream(None) if self._log_filename: logger.set_filename(self._log_filename, csv=self._log_csv) # Add fields to log max_iter_guess = max(self._max_iterations or 0, 10000) max_eval_guess = max_iter_guess logger.add_counter('Iter.', max_value=max_iter_guess) logger.add_counter('Eval.', max_value=max_eval_guess) logger.add_float('Acceptance rate') self._sampler._log_init(logger) logger.add_time('Time m:s') # Start sampling timer = pints.Timer() running = True # Specifying the number of samples we want to get # from the prior at once. It depends on whether we # are using parallelisation and how many workers # are being used. if self._parallel: n_requested_samples = self._n_workers else: n_requested_samples = 1 samples = [] # Sample until we find an acceptable sample while running: accepted_vals = None while accepted_vals is None: # Get points from prior xs = self._sampler.ask(n_requested_samples) # Simulate and get error fxs = evaluator.evaluate(xs) evaluations += self._n_workers # Tell sampler errors and get list of acceptable parameters accepted_vals = self._sampler.tell(fxs) accepted_count += len(accepted_vals) for val in accepted_vals: samples.append(val) iteration += 1 # Log progress if logging and iteration >= next_message: # Log state logger.log(iteration, evaluations, (accepted_count / evaluations)) self._sampler._log_write(logger) logger.log(timer.time()) # Choose next logging point if iteration < self._message_warm_up: next_message = iteration + 1 else: next_message = self._message_interval * ( 1 + iteration // self._message_interval) if iteration >= self._max_iterations: running = False halt_message = ('Halting: Maximum number of iterations (' + str(iteration) + ') reached. Only (' + str(accepted_count) + ') sample were ' + 'obtained') elif accepted_count >= self._n_samples: running = False halt_message = ('Halting: target number of samples (' + str(accepted_count) + ') reached.') # Log final state and show halt message if logging: logger.log(iteration, evaluations) self._sampler._log_write(logger) logger.log(timer.time()) if self._log_to_screen: print(halt_message) samples = np.array(samples) return samples
def run(self, returnLL=False): """ Runs the MCMC sampler(s) and returns a number of markov chains, each representing the distribution of the given log-pdf. """ # Check stopping criteria has_stopping_criterion = False has_stopping_criterion |= (self._max_iterations is not None) if not has_stopping_criterion: raise ValueError('At least one stopping criterion must be set.') # Iteration and evaluation counting iteration = 0 evaluations = 0 # Create evaluator object if self._parallel: # Use at most n_workers workers n_workers = min(self._n_workers, self._chains) evaluator = pints.ParallelEvaluator(self._log_pdf, n_workers=n_workers) else: evaluator = pints.SequentialEvaluator(self._log_pdf) # Initial phase if self._needs_initial_phase: for sampler in self._samplers: sampler.set_initial_phase(True) # Set up progress reporting next_message = 0 # Start logging logging = self._log_to_screen or self._log_filename if logging: if self._log_to_screen: print('Using ' + str(self._samplers[0].name())) print('Generating ' + str(self._chains) + ' chains.') if self._parallel: print('Running in parallel with ' + str(n_workers) + ' worker processess.') else: print('Running in sequential mode.') # Set up logger logger = pints.Logger() if not self._log_to_screen: logger.set_stream(None) if self._log_filename: logger.set_filename(self._log_filename, csv=self._log_csv) # Add fields to log max_iter_guess = max(self._max_iterations or 0, 10000) max_eval_guess = max_iter_guess * self._chains logger.add_counter('Iter.', max_value=max_iter_guess) logger.add_counter('Eval.', max_value=max_eval_guess) for sampler in self._samplers: sampler._log_init(logger) logger.add_time('Time m:s') # Create chains # TODO Pre-allocate? # TODO Thinning # TODO Advanced logging LLs = [] chains = [] # Start sampling timer = pints.Timer() running = True while running: # Initial phase if (self._needs_initial_phase and iteration == self._initial_phase_iterations): for sampler in self._samplers: sampler.set_initial_phase(False) if self._log_to_screen: print('Initial phase completed.') # Get points if self._single_chain: xs = [sampler.ask() for sampler in self._samplers] else: xs = self._samplers[0].ask() # Calculate scores fxs = evaluator.evaluate(xs) # Perform iteration(s) if self._single_chain: samples = np.array( [s.tell(fxs[i]) for i, s in enumerate(self._samplers)]) else: samples = self._samplers[0].tell(fxs) if returnLL: LLs.append(evaluator.evaluate(samples)) chains.append(samples) # Update evaluation count evaluations += len(fxs) # Show progress if logging and iteration >= next_message: # Log state logger.log(iteration, evaluations) for sampler in self._samplers: sampler._log_write(logger) logger.log(timer.time()) # Choose next logging point if iteration < self._message_warm_up: next_message = iteration + 1 else: next_message = self._message_rate * ( 1 + iteration // self._message_rate) # Update iteration count iteration += 1 # # Check stopping criteria # # Maximum number of iterations if (self._max_iterations is not None and iteration >= self._max_iterations): running = False halt_message = ('Halting: Maximum number of iterations (' + str(iteration) + ') reached.') # TODO Add more stopping criteria # Log final state and show halt message if logging: logger.log(iteration, evaluations) for sampler in self._samplers: sampler._log_write(logger) logger.log(timer.time()) if self._log_to_screen: print(halt_message) # Swap axes in chains, to get indices # [chain, iteration, parameter] chains = np.array(chains) chains = chains.swapaxes(0, 1) if returnLL: LLs = np.array(LLs) #LLs = LLs.swapaxes(0, 1) return chains, LLs # Return generated chains return chains