Example #1
0
 def test_maxiter_stops_solve(self):
     # test that if the maximum number of iterations is exceeded
     # the solver stops.
     solver = DifferentialEvolutionSolver(rosen, self.bounds, maxiter=1)
     result = solver.solve()
     assert_equal(result.success, False)
     assert_equal(result.message,
                     'Maximum number of iterations has been exceeded.')
Example #2
0
    def test_exp_runs(self):
        # test whether exponential mutation loop runs
        solver = DifferentialEvolutionSolver(rosen,
                                             self.bounds,
                                             strategy='best1exp',
                                             maxiter=1)

        solver.solve()
Example #3
0
    def setUp(self):
        self.old_seterr = np.seterr(invalid='raise')
        self.limits = np.array([[0., 0.], [2., 2.]])
        self.bounds = [(0., 2.), (0., 2.)]

        self.dummy_solver = DifferentialEvolutionSolver(
            self.quadratic, [(0, 100)])

        # dummy_solver2 will be used to test mutation strategies
        self.dummy_solver2 = DifferentialEvolutionSolver(self.quadratic,
                                                         [(0, 1)],
                                                         popsize=7,
                                                         mutation=0.5)
        # create a population that's only 7 members long
        # [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7]
        population = np.atleast_2d(np.arange(0.1, 0.8, 0.1)).T
        self.dummy_solver2.population = population
 def test_maxiter_none_GH5731(self):
     # Pre 0.17 the previous default for maxiter and maxfun was None.
     # the numerical defaults are now 1000 and np.inf. However, some scripts
     # will still supply None for both of those, this will raise a TypeError
     # in the solve method.
     solver = DifferentialEvolutionSolver(rosen, self.bounds, maxiter=None,
                                          maxfun=None)
     solver.solve()
Example #5
0
    def test_maxfun_stops_solve(self):
        # test that if the maximum number of function evaluations is exceeded
        # during initialisation the solver stops
        solver = DifferentialEvolutionSolver(rosen, self.bounds, maxfun=1,
                                             polish=False)
        result = solver.solve()

        assert_equal(result.nfev, 2)
        assert_equal(result.success, False)
        assert_equal(result.message,
                     'Maximum number of function evaluations has '
                     'been exceeded.')

        # test that if the maximum number of function evaluations is exceeded
        # during the actual minimisation, then the solver stops.
        # Have to turn polishing off, as this will still occur even if maxfun
        # is reached. For popsize=5 and len(bounds)=2, then there are only 10
        # function evaluations during initialisation.
        solver = DifferentialEvolutionSolver(rosen,
                                             self.bounds,
                                             popsize=5,
                                             polish=False,
                                             maxfun=40)
        result = solver.solve()

        assert_equal(result.nfev, 41)
        assert_equal(result.success, False)
        assert_equal(result.message,
                         'Maximum number of function evaluations has '
                              'been exceeded.')

        # now repeat for updating='deferred version
        solver = DifferentialEvolutionSolver(rosen,
                                             self.bounds,
                                             popsize=5,
                                             polish=False,
                                             maxfun=40,
                                             updating='deferred')
        result = solver.solve()

        assert_equal(result.nfev, 40)
        assert_equal(result.success, False)
        assert_equal(result.message,
                         'Maximum number of function evaluations has '
                              'been reached.')
Example #6
0
 def test_deferred_updating(self):
     # check setting of deferred updating, with default workers
     bounds = [(0., 2.), (0., 2.)]
     solver = DifferentialEvolutionSolver(rosen,
                                          bounds,
                                          updating='deferred')
     assert_(solver._updating == 'deferred')
     assert_(solver._mapwrapper._mapfunc is map)
     solver.solve()
Example #7
0
    def test_calculate_population_energies(self):
        # if popsize is 3 then the overall generation has size (6,)
        solver = DifferentialEvolutionSolver(rosen, self.bounds, popsize=3)
        solver._calculate_population_energies(solver.population)
        solver._promote_lowest_energy()
        assert_equal(np.argmin(solver.population_energies), 0)

        # initial calculation of the energies should require 6 nfev.
        assert_equal(solver._nfev, 6)
Example #8
0
 def test_select_samples(self):
     # select_samples should return 5 separate random numbers.
     limits = np.arange(12., dtype='float64').reshape(2, 6)
     bounds = list(zip(limits[0, :], limits[1, :]))
     solver = DifferentialEvolutionSolver(None, bounds, popsize=1)
     candidate = 0
     r1, r2, r3, r4, r5 = solver._select_samples(candidate, 5)
     assert_equal(len(np.unique(np.array([candidate, r1, r2, r3, r4, r5]))),
                  6)
Example #9
0
    def test_parallel(self):
        # smoke test for parallelisation with deferred updating
        bounds = [(0., 2.), (0., 2.)]
        with multiprocessing.Pool(2) as p, DifferentialEvolutionSolver(
                rosen, bounds, updating='deferred', workers=p.map) as solver:
            assert_(solver._mapwrapper.pool is not None)
            assert_(solver._updating == 'deferred')
            solver.solve()

        with DifferentialEvolutionSolver(rosen,
                                         bounds,
                                         updating='deferred',
                                         workers=2) as solver:
            assert_(solver._mapwrapper.pool is not None)
            assert_(solver._updating == 'deferred')
            solver.solve()
        del solver
        gc.collect()  # ensure MapWrapper cleans up properly
 def test_parallel(self):
     # smoke test for parallelisation with deferred updating
     bounds = [(0., 2.), (0., 2.)]
     with DifferentialEvolutionSolver(rosen,
                                      bounds,
                                      updating='deferred',
                                      workers=2) as solver:
         assert_(solver._mapwrapper.pool is not None)
         assert_(solver._updating == 'deferred')
         solver.solve()
    def test_parallel(self):
        # smoke test for parallelisation with deferred updating
        bounds = [(0., 2.), (0., 2.)]
        try:
            p = multiprocessing.Pool(2)
            with DifferentialEvolutionSolver(rosen, bounds,
                                             updating='deferred',
                                             workers=p.map) as solver:
                assert_(solver._mapwrapper.pool is not None)
                assert_(solver._updating == 'deferred')
                solver.solve()
        finally:
            p.close()

        with DifferentialEvolutionSolver(rosen, bounds, updating='deferred',
                                         workers=2) as solver:
            assert_(solver._mapwrapper.pool is not None)
            assert_(solver._updating == 'deferred')
            solver.solve()
Example #12
0
    def _maximize(
            self,
            runhistory: RunHistory,
            stats: Stats,
            num_points: int,
            _sorted: bool=False,
            **kwargs
    ) -> List[Tuple[float, Configuration]]:
        """DifferentialEvolutionSolver

        Parameters
        ----------
        runhistory: ~smac.runhistory.runhistory.RunHistory
            runhistory object
        stats: ~smac.stats.stats.Stats
            current stats object
        num_points: int
            number of points to be sampled
        _sorted: bool
            whether random configurations are sorted according to acquisition function
        **kwargs
            not used

        Returns
        -------
        iterable
            An iterable consistng of
            tuple(acqusition_value, :class:`smac.configspace.Configuration`).
        """


        from scipy.optimize._differentialevolution import DifferentialEvolutionSolver
        configs = []

        def func(x):
            return -self.acquisition_function([Configuration(self.config_space, vector=x)])

        ds = DifferentialEvolutionSolver(func, bounds=[[0, 1], [0, 1]], args=(),
                                    strategy='best1bin', maxiter=1000,
                                    popsize=50, tol=0.01,
                                    mutation=(0.5, 1),
                                    recombination=0.7,
                                    seed=self.rng.randint(1000), polish=True,
                                    callback=None,
                                    disp=False, init='latinhypercube', atol=0)

        rval = ds.solve()
        for pop, val in zip(ds.population, ds.population_energies):
            rc = Configuration(self.config_space, vector=pop)
            rc.origin = 'DifferentialEvolution'
            configs.append((-val, rc))

        configs.sort(key=lambda t: t[0])
        configs.reverse()
        return configs
Example #13
0
    def test_constraint_population_feasibilities(self):
        def constr_f(x):
            return [x[0] + x[1]]

        def constr_f2(x):
            return [x[0]**2 + x[1], x[0] - x[1]]

        nlc = NonlinearConstraint(constr_f, -np.inf, 1.9)

        solver = DifferentialEvolutionSolver(rosen, [(0, 2), (0, 2)],
                                             constraints=(nlc))

        # are population feasibilities correct
        # [0.5, 0.5] corresponds to scaled values of [1., 1.]
        feas, cv = solver._calculate_population_feasibilities(
            np.array([[0.5, 0.5], [1., 1.]]))
        assert_equal(feas, [False, False])
        assert_almost_equal(cv, np.array([[0.1], [2.1]]))
        assert cv.shape == (2, 1)

        nlc2 = NonlinearConstraint(constr_f2, -np.inf, 1.8)
        solver = DifferentialEvolutionSolver(rosen, [(0, 2), (0, 2)],
                                             constraints=(nlc, nlc2))

        feas, cv = solver._calculate_population_feasibilities(
            np.array([[0.5, 0.5], [0.6, 0.5]]))
        assert_equal(feas, [False, False])
        assert_almost_equal(cv, np.array([[0.1, 0.2, 0], [0.3, 0.64, 0]]))

        feas, cv = solver._calculate_population_feasibilities(
            np.array([[0.5, 0.5], [1., 1.]]))
        assert_equal(feas, [False, False])
        assert_almost_equal(cv, np.array([[0.1, 0.2, 0], [2.1, 4.2, 0]]))
        assert cv.shape == (2, 3)

        feas, cv = solver._calculate_population_feasibilities(
            np.array([[0.25, 0.25], [1., 1.]]))
        assert_equal(feas, [True, False])
        assert_almost_equal(cv, np.array([[0.0, 0.0, 0.], [2.1, 4.2, 0]]))
        assert cv.shape == (2, 3)
Example #14
0
    def test_impossible_constraint(self):
        def constr_f(x):
            return np.array([x[0] + x[1]])

        nlc = NonlinearConstraint(constr_f, -np.inf, -1)

        solver = DifferentialEvolutionSolver(rosen, [(0, 2), (0, 2)],
                                             constraints=(nlc),
                                             popsize=3,
                                             seed=1)

        # a UserWarning is issued because the 'trust-constr' polishing is
        # attempted on the least infeasible solution found.
        with warns(UserWarning):
            res = solver.solve()

        assert res.maxcv > 0
        assert not res.success

        # test _promote_lowest_energy works when none of the population is
        # feasible. In this case the solution with the lowest constraint
        # violation should be promoted.
        solver = DifferentialEvolutionSolver(rosen, [(0, 2), (0, 2)],
                                             constraints=(nlc),
                                             polish=False)
        next(solver)
        assert not solver.feasible.all()
        assert not np.isfinite(solver.population_energies).all()

        # now swap two of the entries in the population
        l = 20
        cv = solver.constraint_violation[0]

        solver.population_energies[[0, l]] = solver.population_energies[[l, 0]]
        solver.population[[0, l], :] = solver.population[[l, 0], :]
        solver.constraint_violation[[0, l], :] = (
            solver.constraint_violation[[l, 0], :])

        solver._promote_lowest_energy()
        assert_equal(solver.constraint_violation[0], cv)
    def test_iteration(self):
        # test that DifferentialEvolutionSolver is iterable
        # if popsize is 3 then the overall generation has size (6,)
        solver = DifferentialEvolutionSolver(rosen, self.bounds, popsize=3,
                                             maxfun=12)
        x, fun = next(solver)
        assert_equal(np.size(x, 0), 2)

        # 6 nfev are required for initial calculation of energies, 6 nfev are
        # required for the evolution of the 6 population members.
        assert_equal(solver._nfev, 12)

        # the next generation should halt because it exceeds maxfun
        assert_raises(StopIteration, next, solver)

        # check a proper minimisation can be done by an iterable solver
        solver = DifferentialEvolutionSolver(rosen, self.bounds)
        for i, soln in enumerate(solver):
            x_current, fun_current = soln
            # need to have this otherwise the solver would never stop.
            if i == 1000:
                break

        assert_almost_equal(fun_current, 0)
Example #16
0
    def test_constraint_solve(self):
        def constr_f(x):
            return np.array([x[0] + x[1]])

        nlc = NonlinearConstraint(constr_f, -np.inf, 1.9)

        solver = DifferentialEvolutionSolver(rosen, [(0, 2), (0, 2)],
                                             constraints=(nlc))

        # trust-constr warns if the constraint function is linear
        with warns(UserWarning):
            res = solver.solve()

        assert constr_f(res.x) <= 1.9
        assert res.success
    def test_population_initiation(self):
        # test the different modes of population initiation

        # init must be either 'latinhypercube' or 'random'
        # raising ValueError is something else is passed in
        assert_raises(ValueError, DifferentialEvolutionSolver,
                      *(rosen, self.bounds), **{'init': 'rubbish'})

        solver = DifferentialEvolutionSolver(rosen, self.bounds)

        # check that population initiation:
        # 1) resets _nfev to 0
        # 2) all population energies are np.inf
        solver.init_population_random()
        assert_equal(solver._nfev, 0)
        assert_(np.all(np.isinf(solver.population_energies)))

        solver.init_population_lhs()
        assert_equal(solver._nfev, 0)
        assert_(np.all(np.isinf(solver.population_energies)))
Example #18
0
 def test_best_solution_retrieval(self):
     # test that the getter property method for the best solution works.
     solver = DifferentialEvolutionSolver(self.quadratic, [(-2, 2)])
     result = solver.solve()
     assert_almost_equal(result.x, solver.x)
 def test_quadratic(self):
     # test the quadratic function from object
     solver = DifferentialEvolutionSolver(self.quadratic, [(-100, 100)],
                                          tol=0.02)
     solver.solve()
    def test_population_initiation(self):
        # test the different modes of population initiation

        # init must be either 'latinhypercube' or 'random'
        # raising ValueError is something else is passed in
        assert_raises(ValueError,
                      DifferentialEvolutionSolver,
                      *(rosen, self.bounds),
                      **{'init': 'rubbish'})

        solver = DifferentialEvolutionSolver(rosen, self.bounds)

        # check that population initiation:
        # 1) resets _nfev to 0
        # 2) all population energies are np.inf
        solver.init_population_random()
        assert_equal(solver._nfev, 0)
        assert_(np.all(np.isinf(solver.population_energies)))

        solver.init_population_lhs()
        assert_equal(solver._nfev, 0)
        assert_(np.all(np.isinf(solver.population_energies)))

        solver.init_population_qmc(qmc_engine='halton')
        assert_equal(solver._nfev, 0)
        assert_(np.all(np.isinf(solver.population_energies)))

        solver = DifferentialEvolutionSolver(rosen, self.bounds, init='sobol')
        solver.init_population_qmc(qmc_engine='sobol')
        assert_equal(solver._nfev, 0)
        assert_(np.all(np.isinf(solver.population_energies)))

        # we should be able to initialize with our own array
        population = np.linspace(-1, 3, 10).reshape(5, 2)
        solver = DifferentialEvolutionSolver(rosen, self.bounds,
                                             init=population,
                                             strategy='best2bin',
                                             atol=0.01, seed=1, popsize=5)

        assert_equal(solver._nfev, 0)
        assert_(np.all(np.isinf(solver.population_energies)))
        assert_(solver.num_population_members == 5)
        assert_(solver.population_shape == (5, 2))

        # check that the population was initialized correctly
        unscaled_population = np.clip(solver._unscale_parameters(population),
                                      0, 1)
        assert_almost_equal(solver.population[:5], unscaled_population)

        # population values need to be clipped to bounds
        assert_almost_equal(np.min(solver.population[:5]), 0)
        assert_almost_equal(np.max(solver.population[:5]), 1)

        # shouldn't be able to initialize with an array if it's the wrong shape
        # this would have too many parameters
        population = np.linspace(-1, 3, 15).reshape(5, 3)
        assert_raises(ValueError,
                      DifferentialEvolutionSolver,
                      *(rosen, self.bounds),
                      **{'init': population})

        # provide an initial solution
        # bounds are [(0, 2), (0, 2)]
        x0 = np.random.uniform(low=0.0, high=2.0, size=2)
        solver = DifferentialEvolutionSolver(
            rosen, self.bounds, x0=x0
        )
        # parameters are scaled to unit interval
        assert_allclose(solver.population[0], x0 / 2.0)
Example #21
0
 def _create_DE_solver(self):
     return DifferentialEvolutionSolver(
         self._fom_func,
         list(self.master_controller.bounds(only_fitted=True)),
         callback=self.fit_callback,
         **self.algo_kwargs)
Example #22
0
 def test_converged(self):
     solver = DifferentialEvolutionSolver(rosen, [(0, 2), (0, 2)])
     solver.solve()
     assert_(solver.converged())
Example #23
0
 def test_convergence(self):
     solver = DifferentialEvolutionSolver(rosen, self.bounds, tol=0.2,
                                          polish=False)
     solver.solve()
     assert_(solver.convergence < 0.2)
Example #24
0
    def test__strategy_resolves(self):
        # test that the correct mutation function is resolved by
        # different requested strategy arguments
        solver = DifferentialEvolutionSolver(rosen,
                                             self.bounds,
                                             strategy='best1exp')
        assert_equal(solver.strategy, 'best1exp')
        assert_equal(solver.mutation_func.__name__, '_best1')

        solver = DifferentialEvolutionSolver(rosen,
                                             self.bounds,
                                             strategy='best1bin')
        assert_equal(solver.strategy, 'best1bin')
        assert_equal(solver.mutation_func.__name__, '_best1')

        solver = DifferentialEvolutionSolver(rosen,
                                             self.bounds,
                                             strategy='rand1bin')
        assert_equal(solver.strategy, 'rand1bin')
        assert_equal(solver.mutation_func.__name__, '_rand1')

        solver = DifferentialEvolutionSolver(rosen,
                                             self.bounds,
                                             strategy='rand1exp')
        assert_equal(solver.strategy, 'rand1exp')
        assert_equal(solver.mutation_func.__name__, '_rand1')

        solver = DifferentialEvolutionSolver(rosen,
                                             self.bounds,
                                             strategy='rand2exp')
        assert_equal(solver.strategy, 'rand2exp')
        assert_equal(solver.mutation_func.__name__, '_rand2')

        solver = DifferentialEvolutionSolver(rosen,
                                             self.bounds,
                                             strategy='best2bin')
        assert_equal(solver.strategy, 'best2bin')
        assert_equal(solver.mutation_func.__name__, '_best2')

        solver = DifferentialEvolutionSolver(rosen,
                                             self.bounds,
                                             strategy='rand2bin')
        assert_equal(solver.strategy, 'rand2bin')
        assert_equal(solver.mutation_func.__name__, '_rand2')

        solver = DifferentialEvolutionSolver(rosen,
                                             self.bounds,
                                             strategy='rand2exp')
        assert_equal(solver.strategy, 'rand2exp')
        assert_equal(solver.mutation_func.__name__, '_rand2')

        solver = DifferentialEvolutionSolver(rosen,
                                             self.bounds,
                                             strategy='randtobest1bin')
        assert_equal(solver.strategy, 'randtobest1bin')
        assert_equal(solver.mutation_func.__name__, '_randtobest1')

        solver = DifferentialEvolutionSolver(rosen,
                                             self.bounds,
                                             strategy='randtobest1exp')
        assert_equal(solver.strategy, 'randtobest1exp')
        assert_equal(solver.mutation_func.__name__, '_randtobest1')

        solver = DifferentialEvolutionSolver(rosen,
                                             self.bounds,
                                             strategy='currenttobest1bin')
        assert_equal(solver.strategy, 'currenttobest1bin')
        assert_equal(solver.mutation_func.__name__, '_currenttobest1')

        solver = DifferentialEvolutionSolver(rosen,
                                             self.bounds,
                                             strategy='currenttobest1exp')
        assert_equal(solver.strategy, 'currenttobest1exp')
        assert_equal(solver.mutation_func.__name__, '_currenttobest1')
Example #25
0
 def test_differential_evolution(self):
     # test that the Jmin of DifferentialEvolutionSolver
     # is the same as the function evaluation
     solver = DifferentialEvolutionSolver(self.quadratic, [(-2, 2)])
     result = solver.solve()
     assert_almost_equal(result.fun, self.quadratic(result.x))
Example #26
0
 def test_quadratic(self):
     # test the quadratic function from object
     solver = DifferentialEvolutionSolver(self.quadratic, [(-100, 100)],
                                          tol=0.02)
     solver.solve()
     assert_equal(np.argmin(solver.population_energies), 0)
Example #27
0
def mcmc_sample_model(model, y, beta=1.,
		nwalkers=100, nburnin=200, nprod=800,
		nthreads=1, optimized=False,
		bounds=None,
		return_logpost=False,
		show_progress=False, progress_mod=10):
	"""Markov Chain Monte Carlo sampling interface

	MCMC sampling interface to sample posterior probabilities using the
	:class:`emcee.EnsembleSampler` [#]_.

	.. [#] https://emcee.readthedocs.io

	Arguments
	---------
	model : celerite.GP, george.GP, or sciapy.regress.RegressionModel instance
		The model to draw posterior samples from. It should provide either
		`log_likelihood()` and `log_prior()` functions or be directly callable
		via `__call__()`.
	y : (N,) array_like
		The data to condition the probabilities on.
	beta : float, optional
		Tempering factor for the probability, default: 1.
	nwalkers : int, optional
		The number of MCMC walkers (default: 100). If this number is smaller
		than 4 times the number of parameters, it is multiplied by the number
		of parameters. Otherwise it specifies the number of parameters directly.
	nburnin : int, optional
		The number of burn-in samples to draw, default: 200.
	nprod : int, optional
		The number of production samples to draw, default: 800.
	nthreads : int, optional
		The number of threads to use with a `multiprocessing.Pool`,
		used as `pool` for `emcee.EnsembleSampler`. Default: 1.
	optimized : bool, optional
		Indicate whether the actual (starting) position was determined with an
		optimization algorithm beforehand. If `False` (the default), a
		pre-burn-in run optimizes the starting position. Sampling continues
		from there with the normal burn-in and production runs.
		In that case, latin hypercube sampling is used to distribute the walker
		starting positions equally in parameter space.
	bounds : iterable, optional
		The parameter bounds as a list of (min, max) entries.
		Default: None
	return_logpost : bool, optional
		Indicate whether or not to  return the sampled log probabilities as well.
		Default: False
	show_progress : bool, optional
		Print the percentage of samples every `progress_mod` samples.
		Default: False
	progress_mod : int, optional
		Interval in samples to print the percentage of samples.
		Default: 10

	Returns
	-------
	samples or (samples, logpost) : array_like or tuple
		(nwalkers * nprod, ndim) array of the sampled parameters from the
		production run if return_logpost is `False`.
		A tuple of an (nwalkers * nprod, ndim) array (the same as above)
		and an (nwalkers,) array with the second entry containing the
		log posterior probabilities if return_logpost is `True`.
	"""
	v = model.get_parameter_vector()
	ndim = len(v)
	if nwalkers < 4 * ndim:
		nwalkers *= ndim
	logging.info("MCMC parameters: %s walkers, %s burn-in samples, "
				"%s production samples using %s threads.",
				nwalkers, nburnin, nprod, nthreads)

	if isinstance(model, celerite.GP) or isinstance(model, george.GP):
		mod_func = _lpost
		mod_args = (model, y, beta)
	else:
		mod_func = model
		mod_args = (beta,)

	# Initialize the walkers.
	if not optimized:
		# scipy.optimize's DifferentialEvolutionSolver uses
		# latin hypercube sampling as starting positions.
		# We just use their initialization to avoid duplicating code.
		if bounds is None:
			bounds = model.get_parameter_bounds()
		de_solver = DifferentialEvolutionSolver(_lpost,
					bounds=bounds,
					popsize=nwalkers // ndim)
		# The initial population should reflect latin hypercube sampling
		p0 = de_solver.population
		# fill up to full size in case the number of walkers is not a
		# multiple of the number of parameters
		missing = nwalkers - p0.shape[0]
		p0 = np.vstack([p0] +
			[v + 1e-2 * np.random.randn(ndim) for _ in range(missing)])
	else:
		p0 = np.array([v + 1e-2 * np.random.randn(ndim) for _ in range(nwalkers)])

	# set up the sampling pool
	if nthreads > 1:
		pool = Pool(processes=nthreads)
	else:
		pool = None
	sampler = emcee.EnsembleSampler(nwalkers, ndim, mod_func, args=mod_args,
			pool=pool)

	rst0 = np.random.get_state()

	if not optimized:
		logging.info("Running MCMC fit (%s samples)", nburnin)
		p0, lnp0, rst0, _ = _sample_mcmc(sampler, nburnin, p0, rst0,
				show_progress, progress_mod, debug=True)
		logging.info("MCMC fit finished.")

		p = p0[np.argmax(lnp0)]
		logging.info("Fit max logpost: %s, params: %s, exp(params): %s",
					np.max(lnp0), p, np.exp(p))
		model.set_parameter_vector(p)
		logging.debug("params: %s", model.get_parameter_dict())
		logging.debug("log_likelihood: %s", model.log_likelihood(y))
		p0 = [p + 1e-4 * np.random.randn(ndim) for _ in range(nwalkers)]
		sampler.reset()

	logging.info("Running burn-in (%s samples)", nburnin)
	p0, lnp0, rst0, _ = _sample_mcmc(sampler, nburnin, p0, rst0,
			show_progress, progress_mod)
	logging.info("Burn-in finished.")

	p = p0[np.argmax(lnp0)]
	logging.info("burn-in max logpost: %s, params: %s, exp(params): %s",
				np.max(lnp0), p, np.exp(p))
	model.set_parameter_vector(p)
	logging.debug("params: %s", model.get_parameter_dict())
	logging.debug("log_likelihood: %s", model.log_likelihood(y))
	sampler.reset()

	logging.info("Running production chain (%s samples)", nprod)
	_sample_mcmc(sampler, nprod, p0, rst0, show_progress, progress_mod)
	logging.info("Production run finished.")

	samples = sampler.flatchain
	lnp = sampler.flatlnprobability
	# first column in the blobs are the log likelihoods
	lnlh = np.array(sampler.blobs)[..., 0].ravel().astype(float)
	post_expect_loglh = np.nanmean(np.array(lnlh))
	logging.info("total samples: %s", samples.shape)

	samplmean = np.mean(samples, axis=0)
	logging.info("mean: %s, exp(mean): %s, sqrt(exp(mean)): %s",
			samplmean, np.exp(samplmean), np.sqrt(np.exp(samplmean)))

	samplmedian = np.median(samples, axis=0)
	logging.info("median: %s, exp(median): %s, sqrt(exp(median)): %s",
			samplmedian, np.exp(samplmedian), np.sqrt(np.exp(samplmedian)))

	logging.info("max logpost: %s, params: %s, exp(params): %s",
			np.max(lnp), samples[np.argmax(lnp)],
			np.exp(samples[np.argmax(lnp)]))

	logging.info("AIC: %s", 2 * ndim - 2 * np.max(lnp))
	logging.info("BIC: %s", np.log(len(y)) * ndim - 2 * np.max(lnp))
	logging.info("poor man's evidence 1 sum: %s, mean: %s",
			np.sum(np.exp(lnp)), np.mean(np.exp(lnp)))
	logging.info("poor man's evidence 2 max: %s, std: %s",
			np.max(np.exp(lnp)), np.std(np.exp(lnp)))
	logging.info("poor man's evidence 3: %s",
			np.max(np.exp(lnp)) / np.std(np.exp(lnp)))
	logging.info("poor man's evidence 4 sum: %s",
			logsumexp(lnp, b=1. / lnp.shape[0], axis=0))

	# mode
	model.set_parameter_vector(samples[np.argmax(lnp)])
	log_lh = model.log_likelihood(y)
	# Use the likelihood instead of the posterior
	# https://doi.org/10.3847/1538-3881/aa9332
	logging.info("BIC lh: %s", np.log(len(y)) * ndim - 2 * log_lh)
	# DIC
	sample_deviance = -2 * np.max(lnp)
	deviance_at_sample = -2 * (model.log_prior() + log_lh)
	pd = sample_deviance - deviance_at_sample
	dic = 2 * sample_deviance - deviance_at_sample
	logging.info("max logpost log_lh: %s, AIC: %s, DIC: %s, pd: %s",
			model.log_likelihood(y), 2 * ndim - 2 * log_lh, dic, pd)
	# mean
	model.set_parameter_vector(samplmean)
	log_lh = model.log_likelihood(y)
	log_lh_mean = log_lh
	# DIC
	sample_deviance = -2 * np.nanmean(lnp)
	deviance_at_sample = -2 * (model.log_prior() + log_lh)
	pd = sample_deviance - deviance_at_sample
	dic = 2 * sample_deviance - deviance_at_sample
	logging.info("mean log_lh: %s, AIC: %s, DIC: %s, pd: %s",
			model.log_likelihood(y), 2 * ndim - 2 * log_lh, dic, pd)
	# median
	model.set_parameter_vector(samplmedian)
	log_lh = model.log_likelihood(y)
	# DIC
	sample_deviance = -2 * np.nanmedian(lnp)
	deviance_at_sample = -2 * (model.log_prior() + log_lh)
	dic = 2 * sample_deviance - deviance_at_sample
	pd = sample_deviance - deviance_at_sample
	logging.info("median log_lh: %s, AIC: %s, DIC: %s, pd: %s",
			model.log_likelihood(y), 2 * ndim - 2 * log_lh, dic, pd)
	# (4)--(6) in Ando2011 doi:10.1080/01966324.2011.10737798
	pd_ando = 2 * (log_lh_mean - post_expect_loglh)
	ic5 = - 2 * post_expect_loglh + 2 * pd_ando
	ic6 = - 2 * post_expect_loglh + 2 * ndim
	logging.info("Ando2011: pd: %s, IC(5): %s, IC(6): %s",
			pd_ando, ic5, ic6)

	if return_logpost:
		return samples, lnp
	return samples