-
Notifications
You must be signed in to change notification settings - Fork 429
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
420 additions
and
128 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
from __future__ import division, print_function | ||
|
||
from .move import Move | ||
from .stretch import StretchMove | ||
from .red_blue import RedBlueMove | ||
|
||
__all__ = ["Move", "RedBlueMove", "StretchMove"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
from __future__ import division, print_function | ||
|
||
import numpy as np | ||
|
||
from .mh import MHMove | ||
|
||
__all__ = ["GaussianMove"] | ||
|
||
|
||
class GaussianMove(MHMove): | ||
"""A Metropolis step with a Gaussian proposal function. | ||
Args: | ||
cov: The covariance of the proposal function. This can be a scalar, | ||
vector, or matrix and the proposal will be assumed isotropic, | ||
axis-aligned, or general respectively. | ||
mode (Optional): Select the method used for updating parameters. This | ||
can be one of ``"vector"``, ``"random"``, or ``"sequential"``. The | ||
``"vector"`` mode updates all dimensions simultaneously, | ||
``"random"`` randomly selects a dimension and only updates that | ||
one, and ``"sequential"`` loops over dimensions and updates each | ||
one in turn. | ||
factor (Optional[float]): If provided the proposal will be made with a | ||
standard deviation uniformly selected from the range | ||
``exp(U(-log(factor), log(factor))) * cov``. This is invalid for | ||
the ``"vector"`` mode. | ||
Raises: | ||
ValueError: If the proposal dimensions are invalid or if any of any of | ||
the other arguments are inconsistent. | ||
""" | ||
def __init__(self, cov, mode="vector", factor=None): | ||
# Parse the proposal type. | ||
try: | ||
float(cov) | ||
|
||
except TypeError: | ||
cov = np.atleast_1d(cov) | ||
if len(cov.shape) == 1: | ||
# A diagonal proposal was given. | ||
ndim = len(cov) | ||
proposal = _diagonal_proposal(np.sqrt(cov), factor, mode) | ||
|
||
elif len(cov.shape) == 2 and cov.shape[0] == cov.shape[1]: | ||
# The full, square covariance matrix was given. | ||
ndim = cov.shape[0] | ||
proposal = _proposal(cov, factor, mode) | ||
|
||
else: | ||
raise ValueError("Invalid proposal scale dimensions") | ||
|
||
else: | ||
# This was a scalar proposal. | ||
ndim = None | ||
proposal = _isotropic_proposal(np.sqrt(cov), factor, mode) | ||
|
||
super(GaussianMove, self).__init__(proposal, ndim=ndim) | ||
|
||
|
||
class _isotropic_proposal(object): | ||
|
||
allowed_modes = ["vector", "random", "sequential"] | ||
|
||
def __init__(self, scale, factor, mode): | ||
self.index = 0 | ||
self.scale = scale | ||
if factor is None: | ||
self._log_factor = None | ||
else: | ||
if factor < 1.0: | ||
raise ValueError("'factor' must be >= 1.0") | ||
self._log_factor = np.log(factor) | ||
|
||
if mode not in self.allowed_modes: | ||
raise ValueError(("'{0}' is not a recognized mode. " | ||
"Please select from: {1}") | ||
.format(mode, self.allowed_modes)) | ||
self.mode = mode | ||
|
||
def get_factor(self, rng): | ||
if self._log_factor is None: | ||
return 1.0 | ||
return np.exp(rng.uniform(-self._log_factor, self._log_factor)) | ||
|
||
def get_updated_vector(self, rng, x0): | ||
return x0 + self.get_factor(rng) * self.scale * rng.randn(*(x0.shape)) | ||
|
||
def __call__(self, x0, rng): | ||
nw, nd = x0.shape | ||
xnew = self.get_updated_vector(rng, x0) | ||
if self.mode == "random": | ||
m = (range(nw), rng.randint(x0.shape[-1], size=nw)) | ||
elif self.mode == "sequential": | ||
m = (range(nw), self.index % nd + np.zeros(nw, dtype=int)) | ||
self.index = (self.index + 1) % nd | ||
else: | ||
return xnew, np.zeros(nw) | ||
x = np.array(x0) | ||
x[m] = xnew[m] | ||
return x, np.zeros(nw) | ||
|
||
|
||
class _diagonal_proposal(_isotropic_proposal): | ||
|
||
def get_updated_vector(self, rng, x0): | ||
return x0 + self.get_factor(rng) * self.scale * rng.randn(*(x0.shape)) | ||
|
||
|
||
class _proposal(_isotropic_proposal): | ||
|
||
allowed_modes = ["vector"] | ||
|
||
def get_updated_vector(self, rng, x0): | ||
return x0 + self.get_factor(rng) * rng.multivariate_normal( | ||
np.zeros(len(self.scale)), self.scale) |
Oops, something went wrong.