Ejemplo n.º 1
0
 def _initialise_evaluator(self, f):
     """
     Initialises parallel runners, if desired.
     """
     # Create evaluator object
     if self._parallel:
         # Use at most n_workers workers
         n_workers = self._n_workers
         evaluator = pints.ParallelEvaluator(f, n_workers=n_workers)
     else:
         evaluator = pints.SequentialEvaluator(f)
     return evaluator
Ejemplo n.º 2
0
    def test_parallel(self):

        # Create test data
        xs = np.random.normal(0, 10, 100)
        ys = [f(x) for x in xs]

        # Test parallel evaluator
        e = pints.ParallelEvaluator(f)
        self.assertTrue(np.all(ys == e.evaluate(xs)))

        # Function must be callable
        self.assertRaises(ValueError, pints.ParallelEvaluator, 3)

        # Argument must be sequence
        self.assertRaises(ValueError, e.evaluate, 1)

        # Test args
        e = pints.SequentialEvaluator(f_args, [10, 20])
        self.assertEqual(e.evaluate([1]), [31])

        # Args must be a sequence
        self.assertRaises(ValueError, pints.ParallelEvaluator, f_args, args=1)

        # n-workers must be >0
        self.assertRaises(ValueError, pints.ParallelEvaluator, f, 0)

        # max tasks must be >0
        self.assertRaises(ValueError, pints.ParallelEvaluator, f, 1, 0)

        # Exceptions in called method should trigger halt, cause new exception
        e = pints.ParallelEvaluator(ioerror_on_five, n_workers=2)
        self.assertRaisesRegex(Exception, 'Exception in subprocess',
                               e.evaluate, [1, 2, 5])
        e.evaluate([1, 2])

        # System exit
        e = pints.ParallelEvaluator(system_exit_on_four, n_workers=2)
        self.assertRaisesRegex(Exception, 'Exception in subprocess',
                               e.evaluate, [1, 2, 4])
        e.evaluate([1, 2])
Ejemplo n.º 3
0
    def test_parallel_random(self):
        # Test parallel processes get different random seed, but are
        # reproducible.

        # Ensure that worker processes don't all use the same random sequence
        # To test this, generate a random number in each task, and check that
        # the numbers don't match. With max-tasks-per-worker at 1, they should
        # all be the same without seeding.
        n = 20
        e = pints.ParallelEvaluator(random_int,
                                    n_workers=n,
                                    max_tasks_per_worker=1)
        x = np.array(e.evaluate([0] * n))
        self.assertFalse(np.all(x == x[0]))

        # Without max-tasks-per-worker, we still expect most workers to do 1
        # task, and some to do 2, maybe even three.
        e = pints.ParallelEvaluator(random_int, n_workers=n)
        x = set(e.evaluate([0] * n))
        self.assertTrue(len(x) > n // 2)

        # Getting the same numbers twice should be very unlikely
        x = np.array(e.evaluate([0] * n))
        y = np.array(e.evaluate([0] * n))
        #self.assertFalse(np.all(x) == np.all(y))
        self.assertTrue(len(set(x) | set(y)) > n // 2)

        # But with seeding we expect the same result twice
        np.random.seed(123)
        x = np.array(e.evaluate([0] * n))
        np.random.seed(123)
        y = np.array(e.evaluate([0] * n))
        self.assertTrue(np.all(x) == np.all(y))

        # Even with many more tasks than workers
        e = pints.ParallelEvaluator(random_int, n_workers=3)
        np.random.seed(123)
        x = np.array(e.evaluate([0] * 100))
        np.random.seed(123)
        y = np.array(e.evaluate([0] * 100))
Ejemplo n.º 4
0
def waic(problem, samples, current, sigma):

    log_pp_obj = log_posterior_pointwise(problem, current, sigma)
    n_workers = pints.ParallelEvaluator.cpu_count()
    evaluator_log_pp = pints.ParallelEvaluator(log_pp_obj, n_workers=n_workers)
    log_pp = evaluator_log_pp.evaluate(samples)
    log_pp = np.asarray(log_pp)
    if log_pp.size == 0:
        raise ValueError('The model does not contain observed values.')

    lppd_i = logsumexp(log_pp, axis=0, b=1.0 / log_pp.shape[0])

    vars_lpd = np.var(log_pp, axis=0)
    warn_mg = 0
    if np.any(vars_lpd > 0.4):
        warnings.warn("""For one or more samples the posterior variance of the
        log predictive densities exceeds 0.4. This could be indication of
        WAIC starting to fail see http://arxiv.org/abs/1507.04544 for details
        """)
        warn_mg = 1

    waic_i = -2 * (lppd_i - vars_lpd)

    waic_se = np.sqrt(len(waic_i) * np.var(waic_i))

    waic = np.sum(waic_i)

    p_waic = np.sum(vars_lpd)
    pointwise = False
    if pointwise:
        if np.equal(waic, waic_i).all():
            warnings.warn("""The point-wise WAIC is the same with the sum WAIC,
            please double check the Observed RV in your model to make sure it
            returns element-wise logp.
            """)
        WAIC_r = namedtuple('WAIC_r',
                            'WAIC, WAIC_se, p_WAIC, var_warn, WAIC_i')
        return WAIC_r(waic, waic_se, p_waic, warn_mg, waic_i)
    else:
        WAIC_r = namedtuple('WAIC_r', 'WAIC, WAIC_se, p_WAIC, var_warn')
        return WAIC_r(waic, waic_se, p_waic, warn_mg)
Ejemplo n.º 5
0
    def test_covariance_matrix_and_mean(self):
        # Tests getting the covariance matrix and mean

        r, x, s, b = self.problem()
        opt = method(x)

        e = pints.ParallelEvaluator(r)
        for i in range(10):
            opt.tell(e.evaluate(opt.ask()))

        # Get covariance matrix: check shape and symmetry
        C = opt.cov()
        self.assertEqual(C.shape, (2, 2))
        self.assertTrue(np.all(C == C.T))

        # Get decomposition
        R, S = opt.cov(decomposed=True)
        error = np.max(np.abs(C - R.dot(S).dot(S).dot(R.T)))
        self.assertLess(error, 1e-15)

        # Get mean
        mean = opt.mean()
        self.assertEqual(mean.shape, (2, ))
Ejemplo n.º 6
0
    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
Ejemplo n.º 7
0
    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
Ejemplo n.º 8
0
def sample_initial_points(function, n_points, random_sampler=None,
                          boundaries=None, max_tries=50, parallel=False,
                          n_workers=None):
    """
    Samples ``n_points`` parameter values to use as starting points in a
    sampling or optimisation routine on the given ``function``.

    How the initial points are determined depends on the arguments supplied. In
    order of precedence:

    1. If a method ``random_sampler`` is provided then this will be used to
       draw the random samples.
    2. If no sampler method is given but ``function`` is a
       :class:`LogPosterior` then the method ``function.log_prior().sample()``
       will be used.
    3. If no sampler method is supplied and ``function`` is not a
       :class:`LogPosterior` and if ``boundaries`` are provided then the method
       ``boundaries.sample()`` will be used to draw samples.

    A ``ValueError`` is raised if none of the above options are available.

    Each sample ``x`` is tested to ensure that ``function(x)`` returns a finite
    result within ``boundaries`` if these are supplied. If not, a new sample
    will be drawn. This is repeated at most ``max_tries`` times, after which an
    error is raised.

    Parameters
    ----------
    function :
        A :class:`pints.ErrorMeasure` or a :class:`pints.LogPDF` that
        evaluates points in the parameter space. If the latter, it is optional
        that ``function`` be of type :class:`LogPosterior`.
    n_points : int
        The number of initial values to generate.
    random_sampler :
        A function that when called returns draws from a probability
        distribution of the same dimensionality as ``function``. The only
        argument to this function should be an integer specifying the number of
        draws.
    boundaries :
        An optional set of boundaries on the parameter space of class
        :class:`pints.Boundaries`.
    max_tries : int
        Number of attempts to find a finite initial value across all
        ``n_points``. By default this is 50 per point.
    parallel : bool
        Whether to evaluate ``function`` in parallel (defaults to False).
    n_workers : int
        Number of workers on which to run parallel evaluation.
    """
    is_not_logpdf = not isinstance(function, pints.LogPDF)
    is_not_errormeasure = not isinstance(function, pints.ErrorMeasure)

    # Check function
    if is_not_logpdf and is_not_errormeasure:
        raise ValueError(
            'function must be either pints.LogPDF or pints.ErrorMeasure.')

    # Check boundaries
    if boundaries is not None:
        if not isinstance(boundaries, pints.Boundaries):
            raise ValueError('boundaries must be a pints.Boundaries object.')
        elif boundaries.n_parameters() != function.n_parameters():
            raise ValueError('boundaries must match dimension of function.')

    # Check or set random sampler
    if random_sampler is None:
        if isinstance(function, pints.LogPosterior):
            random_sampler = function.log_prior().sample
        elif boundaries is not None:
            random_sampler = boundaries.sample
        else:
            raise ValueError(
                'If function is not a pints.LogPosterior and no boundaries'
                ' are given then a random_sampler must be supplied.')
    elif not callable(random_sampler):
        raise ValueError(
            'random_sampler must be a callable function, if supplied.')

    # Check number of initial points
    if n_points < 1:
        raise ValueError('Number of initial points must be 1 or more.')

    # Set up parallelisation
    if parallel:
        n_workers = min(pints.ParallelEvaluator.cpu_count(), n_points)
        evaluator = pints.ParallelEvaluator(function, n_workers=n_workers)
    else:
        evaluator = pints.SequentialEvaluator(function)

    # Go!
    x0 = []
    n_tries = 0
    while len(x0) < n_points and n_tries < max_tries:
        xs = random_sampler(n_points - len(x0))
        fxs = evaluator.evaluate(xs)
        for i, x in enumerate(xs):
            fx = fxs[i]
            if np.isfinite(fx):
                if boundaries is None or boundaries.check(x):
                    x0.append(x)
        n_tries += 1

    if len(x0) < n_points:
        raise RuntimeError(
            'Initialisation failed since function not finite or within ' +
            'bounds at initial points after ' + str(max_tries) + ' attempts.')
    return x0
Ejemplo n.º 9
0
    def run(self):
        """See :meth:`Optimiser.run()`."""

        # Default search parameters
        # TODO Allow changing before run() with method call
        parallel = True

        # Search is terminated after max_iter iterations
        # TODO Allow changing before run() with method call
        max_iter = 10000

        # Or if the result doesn't change significantly for a while
        # TODO Allow changing before run() with method call
        max_unchanged_iterations = 100
        # TODO Allow changing before run() with method call
        min_significant_change = 1e-11
        # TODO Allow changing before run() with method call
        unchanged_iterations = 0

        # Parameter space dimension
        d = self._dimension

        # Population size
        # TODO Allow changing before run() with method call
        # If parallel, round up to a multiple of the reported number of cores
        n = 4 + int(3 * np.log(d))
        if parallel:
            cpu_count = multiprocessing.cpu_count()
            n = (((n - 1) // cpu_count) + 1) * cpu_count

        # Set up progress reporting in verbose mode
        nextMessage = 0
        if self._verbose:
            if parallel:
                print('Running in parallel mode with population size ' +
                      str(n))
            else:
                print('Running in sequential mode with population size ' +
                      str(n))

        # Apply wrapper to implement boundaries
        if self._boundaries is None:

            def xtransform(x):
                return x
        else:
            xtransform = pints.TriangleWaveTransform(self._boundaries)

        # Create evaluator object
        if parallel:
            evaluator = pints.ParallelEvaluator(self._function)
        else:
            evaluator = pints.SequentialEvaluator(self._function)

        # Learning rates
        # TODO Allow changing before run() with method call
        eta_mu = 1
        # TODO Allow changing before run() with method call
        eta_A = 0.6 * (3 + np.log(d)) * d**-1.5

        # Pre-calculated utilities
        us = np.maximum(0, np.log(n / 2 + 1) - np.log(1 + np.arange(n)))
        us /= np.sum(us)
        us -= 1 / n

        # Center of distribution
        mu = np.array(self._x0, copy=True)

        # Initial square root of covariance matrix
        A = np.eye(d) * self._sigma0

        # Identity matrix for later use
        I = np.eye(d)

        # Best solution found
        xbest = mu
        fbest = float('inf')

        # Start running
        for iteration in range(1, 1 + max_iter):

            # Create new samples
            zs = np.array([np.random.normal(0, 1, d) for i in range(n)])
            xs = np.array([mu + np.dot(A, zs[i]) for i in range(n)])

            # Evaluate at the samples
            fxs = evaluator.evaluate(xtransform(xs))

            # Order the normalized samples according to the scores
            order = np.argsort(fxs)
            zs = zs[order]

            # Update center
            Gd = np.dot(us, zs)
            mu += eta_mu * np.dot(A, Gd)

            # Update best if needed
            if fxs[order[0]] < fbest:

                # Check if this counts as a significant change
                fnew = fxs[order[0]]
                if np.sum(np.abs(fnew - fbest)) < min_significant_change:
                    unchanged_iterations += 1
                else:
                    unchanged_iterations = 0

                # Update best
                xbest = xs[order[0]]
                fbest = fnew

            else:
                unchanged_iterations += 1

            # Show progress in verbose mode:
            if self._verbose and iteration >= nextMessage:
                print(str(iteration) + ': ' + str(fbest))
                if iteration < 3:
                    nextMessage = iteration + 1
                else:
                    nextMessage = 20 * (1 + iteration // 20)

            # Stop if no change for too long
            if unchanged_iterations >= max_unchanged_iterations:
                if self._verbose:
                    print('Halting: No significant change for ' +
                          str(unchanged_iterations) + ' iterations.')
                break

            # Update root of covariance matrix
            Gm = np.dot(np.array([np.outer(z, z).T - I for z in zs]).T, us)
            A *= scipy.linalg.expm(np.dot(0.5 * eta_A, Gm))

        # Show stopping criterion
        if self._verbose and unchanged_iterations < max_unchanged_iterations:
            print('Halting: Maximum iterations reached.')

        # Get final score at mu
        fmu = self._function(xtransform(mu))
        if fmu < fbest:
            if self._verbose:
                print('Final score at mu beats best sample')
            xbest = mu
            fbest = fmu

        # Show final value
        if self._verbose:
            print(str(iteration) + ': ' + str(fbest))

        # Return best solution
        return xtransform(xbest), fbest
Ejemplo n.º 10
0
    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
Ejemplo n.º 11
0
    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
Ejemplo n.º 12
0
    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
Ejemplo n.º 13
0
    def run(self):
        """See :meth:`Optimiser.run()`."""

        # Global/local search balance
        # TODO Allow changing before run() with method call
        r = 0.5

        # Check at least one stopping criterion is set
        if (self._max_iterations == 0 and self._max_unchanged_iterations == 0):
            raise ValueError('At least one stopping criterion must be set.')

        # Unchanged iterations count (used for stopping or just for
        # information)
        unchanged_iterations = 0

        # Parameter space dimension
        d = self._dimension

        # Population size
        # TODO Allow changing before run() with method call
        # If parallel, round up to a multiple of the reported number of cores
        n = 4 + int(3 * np.log(d))
        if self._parallel:
            cpu_count = multiprocessing.cpu_count()
            n = min(3, (((n - 1) // cpu_count) + 1)) * cpu_count

        # Set up progress reporting in verbose mode
        nextMessage = 0
        if self._verbose:
            if self._parallel:
                print('Running in parallel mode with population size ' +
                      str(n))
            else:
                print('Running in sequential mode with population size ' +
                      str(n))

        # Set parameters based on global/local balance r
        amax = 4.1
        almax = r * amax
        agmax = amax - almax

        # Initialize swarm
        xs = []  # Particle coordinate vectors
        vs = []  # Particle velocity vectors
        fs = []  # Particle scores
        fl = []  # Best local score
        pl = []  # Best local position
        fg = 0  # Best global score
        pg = 0  # Best global position

        # Set initial positions
        xs.append(np.array(self._x0, copy=True))
        if self._boundaries is None:
            for i in range(1, n):
                xs.append(np.random.normal(self._x0, self._sigma0))
        else:
            for i in range(1, n):
                xs.append(self._boundaries._lower +
                          np.random.uniform(0, 1, d) *
                          (self._boundaries._upper - self._boundaries._lower))

        # Set initial velocities
        for i in range(n):
            vs.append(self._sigma0 * np.random.uniform(0, 1, d))

        # Set initial scores and local best
        for i in range(n):
            fs.append(float('inf'))
            fl.append(float('inf'))
            pl.append(xs[i])

        # Set global best position and score
        fg = float('inf')
        pg = xs[0]

        # Apply wrapper to score function to implement boundaries
        function = self._function
        if self._boundaries is not None:
            function = pints.InfBoundaryTransform(function, self._boundaries)

        # Create evaluator object
        if self._parallel:
            evaluator = pints.ParallelEvaluator(self._function)
        else:
            evaluator = pints.SequentialEvaluator(self._function)

        # Start searching
        running = True
        iteration = 0
        while running:
            # Calculate scores
            fs = evaluator.evaluate(xs)

            # Update particles
            for i in range(n):
                # Update best local position and score
                if fs[i] < fl[i]:
                    fl[i] = fs[i]
                    pl[i] = np.array(xs[i], copy=True)

                # Calculate "velocity"
                al = np.random.uniform(0, almax, d)
                ag = np.random.uniform(0, agmax, d)
                vs[i] += al * (pl[i] - xs[i]) + ag * (pg - xs[i])

                # Update position
                e = xs[i] + vs[i]
                if self._boundaries is not None:
                    # To reduce the amount of time spent outside the bounds of
                    # the search space, the velocity of any particle outside
                    # the bounds is reduced by a factor
                    #  (1 / (1 + number of boundary violations)).
                    if not self._boundaries.check(e):
                        vs[i] *= 1 / (1 + np.sum(e))
                xs[i] += vs[i]

            # Update global best
            i = np.argmin(fl)
            if fl[i] < fg:
                # Check if this counts as a significant change
                fnew = fl[i]
                if np.sum(np.abs(fnew - fg)) < self._min_significant_change:
                    unchanged_iterations += 1
                else:
                    unchanged_iterations = 0

                # Update best
                fg = fnew
                pg = np.array(pl[i], copy=True)
            else:
                unchanged_iterations += 1

            # Show progress in verbose mode:
            if self._verbose and iteration >= nextMessage:
                print(str(iteration) + ': ' + str(fg))
                if iteration < 3:
                    nextMessage = iteration + 1
                else:
                    nextMessage = 20 * (1 + iteration // 20)

            # Update iteration count
            iteration += 1

            # Check stopping criteria
            # Maximum number of iterations
            if self._max_iterations and iteration >= self._max_iterations:
                running = False
                if self._verbose:
                    print('Halting: Maximum number of iterations (' +
                          str(iteration) + ' reached.')

            # Maximum number of iterations without significant change
            if (self._max_unchanged_iterations and
                    unchanged_iterations >= self._max_unchanged_iterations):
                running = False
                if self._verbose:
                    print('Halting: No significant change for ' +
                          str(unchanged_iterations) + ' iterations.')

        # Show final value
        if self._verbose:
            print(str(iteration) + ': ' + str(fg))

        # Return best position and score
        return pg, fg
Ejemplo n.º 14
0
    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
Ejemplo n.º 15
0
    def run(self):
        """See: :meth:`pints.Optimiser.run()`."""

        # Import cma (may fail!)
        # Only the first time this is called in a running program incurs
        # much overhead.
        import cma

        # Get BestSolution in cma 1.x and 2.x
        # try:
        #    from cma import BestSolution
        # except ImportError:
        #    from cma.optimization_tools import BestSolution

        # Default search parameters
        # TODO Allow changing before run() with method call
        parallel = True

        # Parameter space dimension
        d = self._dimension

        # Population size
        # TODO Allow changing before run() with method call
        # If parallel, round up to a multiple of the reported number of cores
        # In IPOP-CMAES, this will be used as the _initial_ population size
        n = 4 + int(3 * np.log(d))
        if parallel:
            cpu_count = multiprocessing.cpu_count()
            n = (((n - 1) // cpu_count) + 1) * cpu_count

        # Search is terminated after max_iter iterations
        # TODO Allow changing before run() with method call
        max_iter = 10000
        # CMA-ES default: 100 + 50 * (d + 3)**2 // n**0.5

        # Or if successive iterations do not produce a significant change
        # TODO Allow changing before run() with method call
        # max_unchanged_iterations = 100
        min_significant_change = 1e-11
        # unchanged_iterations = 0
        # CMA-ES max_unchanged_iterations fixed value: 10 + 30 * d / n

        # Create evaluator object
        if parallel:
            evaluator = pints.ParallelEvaluator(self._function)
        else:
            evaluator = pints.SequentialEvaluator(self._function)

        # Set up simulation
        options = cma.CMAOptions()

        # Set boundaries
        if self._boundaries is not None:
            options.set(
                'bounds',
                [list(self._boundaries._lower),
                 list(self._boundaries._upper)])

        # Set stopping criteria
        options.set('maxiter', max_iter)
        options.set('tolfun', min_significant_change)
        # options.set('ftarget', target)

        # Tell CMA not to worry about growing step sizes too much
        options.set('tolfacupx', 10000)

        # CMA-ES wants a single standard deviation as input, use the smallest
        # in the vector (if the user passed in a scalar, this will be the
        # value used).
        sigma0 = np.min(self._sigma0)

        # Tell cma-es to be quiet
        if not self._verbose:
            options.set('verbose', -9)
        # Set population size
        options.set('popsize', n)
        if self._verbose:
            print('Population size ' + str(n))

        # Search
        es = cma.CMAEvolutionStrategy(self._x0, sigma0, options)
        while not es.stop():
            candidates = es.ask()
            es.tell(candidates, evaluator.evaluate(candidates))
            if self._verbose:
                es.disp()

        # Show result
        if self._verbose:
            es.result_pretty()

        # Get solution
        x = es.result.xbest
        fx = es.result.fbest

        # No result found? Then return hint and score of hint
        if x is None:
            return self._x0, self._function(self._x0)

        # Return proper result
        return x, fx
Ejemplo n.º 16
0
    def _run(self, result, log_path):

        import pints
        import numpy as np

        # Store method and iterations
        result['method'] = self._method

        # Get method class
        method = getattr(pints, self._method)

        # Get problem
        score, xtrue, x0, sigma0, boundaries = self._problem()

        # Evaluate at true parameters
        ftrue = score(xtrue)

        # Create optimiser
        optimiser = method(x0, sigma0, boundaries)

        # Count iterations and function evaluations
        iterations = 0
        evaluations = 0
        unchanged_iterations = 0

        # Create parallel evaluator
        n_workers = pints.ParallelEvaluator.cpu_count()
        if isinstance(optimiser, pints.PopulationBasedOptimiser):
            n_workers = min(n_workers, optimiser.population_size())
        evaluator = pints.ParallelEvaluator(score, n_workers=n_workers)

        # Keep track of best position and score
        fbest = float('inf')

        # Start searching
        evals = []
        frels = []

        running = True
        while running:
            xs = optimiser.ask()
            fs = evaluator.evaluate(xs)
            optimiser.tell(fs)

            # Check if new best found
            fnew = optimiser.fbest()
            if fnew < fbest:
                # Check if this counts as a significant change
                if np.abs(fnew - fbest) < 1e-9:
                    unchanged_iterations += 1
                else:
                    unchanged_iterations = 0

                # Update best position
                fbest = fnew
            else:
                unchanged_iterations += 1

            # Update iteration and evaluation count
            iterations += 1
            evaluations += len(fs)

            # Log evaluations and relative score
            evals.append(evaluations)
            frels.append(fbest / ftrue)

            # Maximum number of iterations
            if iterations >= 5000:
                running = False

            # Maximum number of iterations without significant change
            if unchanged_iterations >= 200:
                running = False

            # Error in optimiser
            error = optimiser.stop()
            if error:
                running = False

        # Store solution
        result['xbest'] = optimiser.xbest()
        result['fbest'] = fbest

        # Store solution, relative to likelihood at 'true' solution
        # This should be 1 or below 1 if the optimum was found
        result['fbest_relative'] = fbest / ftrue

        # Store convergence information
        result['evals'] = evals
        result['frels'] = frels

        # Store status
        result['status'] = 'done'