diff options
author | RedRuM <44142765+zoj613@users.noreply.github.com> | 2019-08-17 17:50:56 +0200 |
---|---|---|
committer | RedRuM <44142765+zoj613@users.noreply.github.com> | 2019-08-17 17:50:56 +0200 |
commit | e6f9a354ff3e2354d343493f6463328895f702c2 (patch) | |
tree | 07db57859b734fab6b49e034d2ec9501fa86111a | |
parent | 31de73ba60e7128aced80e65c8e28b28e06bc7f6 (diff) | |
download | numpy-e6f9a354ff3e2354d343493f6463328895f702c2.tar.gz |
REV: Undo all changes to codebase
-rw-r--r-- | numpy/random/generator.pyx | 164 | ||||
-rw-r--r-- | numpy/random/tests/test_generator_mt19937.py | 58 |
2 files changed, 72 insertions, 150 deletions
diff --git a/numpy/random/generator.pyx b/numpy/random/generator.pyx index f7e1801d8..c7432d8c1 100644 --- a/numpy/random/generator.pyx +++ b/numpy/random/generator.pyx @@ -95,7 +95,6 @@ cdef class Generator: cdef binomial_t _binomial cdef object lock _poisson_lam_max = POISSON_LAM_MAX - cdef public object multivariate_normal def __init__(self, bit_generator): self._bit_generator = bit_generator @@ -106,8 +105,6 @@ cdef class Generator: raise ValueError("Invalid bit generator'. The bit generator must " "be instantiated.") self._bitgen = (<bitgen_t *> PyCapsule_GetPointer(capsule, name))[0] - self.multivariate_normal = self._get_multivariate_normal_instance() - self.multivariate_normal.__doc__ = self._get_multivariate_normal_instance.__doc__ self.lock = bit_generator.lock def __repr__(self): @@ -3333,7 +3330,8 @@ cdef class Generator: 0.0, '', CONS_NONE) # Multivariate distributions: - def _get_multivariate_normal_instance(self): + def multivariate_normal(self, mean, cov, size=None, check_valid='warn', + tol=1e-8): """ multivariate_normal(mean, cov, size=None, check_valid='warn', tol=1e-8) @@ -3390,8 +3388,8 @@ cdef class Generator: Instead of specifying the full covariance matrix, popular approximations include: - - Spherical covariance (`cov` is a multiple of the identity matrix) - - Diagonal covariance (`cov` has non-negative elements, and only on + - Spherical covariance (`cov` is a multiple of the identity matrix) + - Diagonal covariance (`cov` has non-negative elements, and only on the diagonal) This geometrical property can be seen in two dimensions by plotting @@ -3415,9 +3413,9 @@ cdef class Generator: References ---------- .. [1] Papoulis, A., "Probability, Random Variables, and Stochastic - Processes," 3rd ed., New York: McGraw-Hill, 1991. + Processes," 3rd ed., New York: McGraw-Hill, 1991. .. [2] Duda, R. O., Hart, P. E., and Stork, D. G., "Pattern - Classification," 2nd ed., New York: Wiley, 2001. + Classification," 2nd ed., New York: Wiley, 2001. Examples -------- @@ -3434,101 +3432,67 @@ cdef class Generator: [True, True] # random """ - class _MultivariateNormal: + from numpy.dual import svd - def __call__(inner_self, mean, cov, size=None, check_valid='warn', - tol=1e-8, method='svd'): - """sample from a multivariate normal distribution""" - x, final_shape, mean, cov = inner_self.input_checks(mean, cov, - size, method) - - # GH10839, ensure double to make tol meaningful - cov = cov.astype(np.double) - if method == 'svd': - from numpy.dual import svd - (u, s, v) = svd(cov) - elif method == 'eigh': - from numpy.dual import eigh - (s, v) = eigh(cov) - else: - from numpy.dual import cholesky - l = cholesky(cov) - - if check_valid != 'ignore' and method != 'cholesky': - if check_valid != 'warn' and check_valid != 'raise': - raise ValueError( - "check_valid must equal 'warn', 'raise', or 'ignore'") - if method == 'svd': - psd = np.allclose(np.dot(v.T * s, v), cov, rtol=tol, atol=tol) - else: - psd = np.all(s >= 0) - if not psd: - if check_valid == 'warn': - warnings.warn("covariance is not positive-semidefinite.", - RuntimeWarning) - else: - raise ValueError( - "covariance is not positive-semidefinite.") - - if method == 'cholesky': - x = np.dot(x, l) - else: - x = np.dot(x, np.sqrt(s)[:, None] * v) - x += mean - x.shape = tuple(final_shape) - return x - - def input_checks(inner_self, mean, cov, size, method): - """Check validity of inputs and determine shape of output - given the dimensions of `size` input. - """ - if method not in {'eigh', 'svd', 'cholesky'}: - raise ValueError( - "mode must to be one of {'eigh', 'svd', 'cholesky'}") - - # Check preconditions on arguments - mean = np.array(mean) - cov = np.array(cov) - if size is None: - shape = [] - elif isinstance(size, (int, long, np.integer)): - shape = [size] - else: - shape = size - - if len(mean.shape) != 1: - raise ValueError("mean must be 1 dimensional") - if (len(cov.shape) != 2) or (cov.shape[0] != cov.shape[1]): - raise ValueError("cov must be 2 dimensional and square") - if mean.shape[0] != cov.shape[0]: - raise ValueError("mean and cov must have same length") - - # Compute shape of output and create a matrix of independent - # standard normally distributed random numbers. The matrix has rows - # with the same length as mean and as many rows are necessary to - # form a matrix of shape final_shape. - final_shape = list(shape[:]) - final_shape.append(mean.shape[0]) - x = self.standard_normal(final_shape).reshape(-1, mean.shape[0]) - return x, final_shape, mean, cov - - def from_factor(inner_self, mean, factor, size=None, method='svd'): - """sample from a multivariate normal distribution using a - factor matrix of its covariance. - """ - x, final_shape, mean, factor = inner_self.input_checks(mean, - factor, - size, - method) - - if method == 'cholesky' and np.tril(factor, k=-1) == 0: - x = mean + np.dot(factor.T, x.T).T + # Check preconditions on arguments + mean = np.array(mean) + cov = np.array(cov) + if size is None: + shape = [] + elif isinstance(size, (int, long, np.integer)): + shape = [size] + else: + shape = size + + if len(mean.shape) != 1: + raise ValueError("mean must be 1 dimensional") + if (len(cov.shape) != 2) or (cov.shape[0] != cov.shape[1]): + raise ValueError("cov must be 2 dimensional and square") + if mean.shape[0] != cov.shape[0]: + raise ValueError("mean and cov must have same length") + + # Compute shape of output and create a matrix of independent + # standard normally distributed random numbers. The matrix has rows + # with the same length as mean and as many rows are necessary to + # form a matrix of shape final_shape. + final_shape = list(shape[:]) + final_shape.append(mean.shape[0]) + x = self.standard_normal(final_shape).reshape(-1, mean.shape[0]) + + # Transform matrix of standard normals into matrix where each row + # contains multivariate normals with the desired covariance. + # Compute A such that dot(transpose(A),A) == cov. + # Then the matrix products of the rows of x and A has the desired + # covariance. Note that sqrt(s)*v where (u,s,v) is the singular value + # decomposition of cov is such an A. + # + # Also check that cov is positive-semidefinite. If so, the u.T and v + # matrices should be equal up to roundoff error if cov is + # symmetric and the singular value of the corresponding row is + # not zero. We continue to use the SVD rather than Cholesky in + # order to preserve current outputs. Note that symmetry has not + # been checked. + + # GH10839, ensure double to make tol meaningful + cov = cov.astype(np.double) + (u, s, v) = svd(cov) + + if check_valid != 'ignore': + if check_valid != 'warn' and check_valid != 'raise': + raise ValueError("check_valid must equal 'warn', 'raise', or 'ignore'") + + psd = np.allclose(np.dot(v.T * s, v), cov, rtol=tol, atol=tol) + if not psd: + if check_valid == 'warn': + warnings.warn("covariance is not positive-semidefinite.", + RuntimeWarning) else: - x = mean + np.dot(factor, x.T).T - x.shape = tuple(final_shape) - return x + raise ValueError("covariance is not positive-semidefinite.") - return _MultivariateNormal() + x = np.dot(x, np.sqrt(s)[:, None] * v) + x += mean + x.shape = tuple(final_shape) + return x def multinomial(self, object n, object pvals, size=None): """ diff --git a/numpy/random/tests/test_generator_mt19937.py b/numpy/random/tests/test_generator_mt19937.py index b720a7b39..a962fe84e 100644 --- a/numpy/random/tests/test_generator_mt19937.py +++ b/numpy/random/tests/test_generator_mt19937.py @@ -3,7 +3,6 @@ import sys import pytest import numpy as np -from numpy.linalg import LinAlgError from numpy.testing import ( assert_, assert_raises, assert_equal, assert_warns, assert_no_warnings, assert_array_equal, @@ -983,16 +982,7 @@ class TestRandomDist(object): mean = (.123456789, 10) cov = [[1, 0], [0, 1]] size = (3, 2) - actual_svd = random.multivariate_normal(mean, cov, size) - # the seed needs to be reset for each method - random = Generator(MT19937(self.seed)) - actual_eigh = random.multivariate_normal(mean, cov, size, method='eigh') - random = Generator(MT19937(self.seed)) - actual_chol = random.multivariate_normal(mean, cov, size, - method='cholesky') - # the factor matrix is the same for all methods - random = Generator(MT19937(self.seed)) - actual_factor = random.multivariate_normal.from_factor(mean, cov, size) + actual = random.multivariate_normal(mean, cov, size) desired = np.array([[[-1.747478062846581, 11.25613495182354 ], [-0.9967333370066214, 10.342002097029821 ]], [[ 0.7850019631242964, 11.181113712443013 ], @@ -1000,62 +990,30 @@ class TestRandomDist(object): [[ 0.7130260107430003, 9.551628690083056 ], [ 0.7127098726541128, 11.991709234143173 ]]]) - assert_array_almost_equal(actual_svd, desired, decimal=15) - assert_array_almost_equal(actual_eigh, desired, decimal=15) - assert_array_almost_equal(actual_chol, desired, decimal=15) + assert_array_almost_equal(actual, desired, decimal=15) # Check for default size, was raising deprecation warning - random = Generator(MT19937(self.seed)) - actual_svd = random.multivariate_normal(mean, cov) - random = Generator(MT19937(self.seed)) - actual_eigh = random.multivariate_normal(mean, cov, method='eigh') - random = Generator(MT19937(self.seed)) - actual_chol = random.multivariate_normal(mean, cov, method='cholesky') - # the factor matrix is the same for all methods - random = Generator(MT19937(self.seed)) - actual_factor = random.multivariate_normal.from_factor(mean, cov) - desired = np.array([-1.747478062846581, 11.25613495182354]) - assert_array_almost_equal(actual_svd, desired, decimal=15) - assert_array_almost_equal(actual_eigh, desired, decimal=15) - assert_array_almost_equal(actual_chol, desired, decimal=15) - assert_array_almost_equal(actual_factor, desired, decimal=15) - - # test if raises ValueError when non-square factor is used - non_square_factor = [[1, 0], [0, 1], [1, 0]] - assert_raises(ValueError, random.multivariate_normal.from_factor, - mean, non_square_factor) - - # Check that non symmetric covariance input raises exception when - # check_valid='raises' if using default svd method. - mean = [0, 0] - cov = [[1, 2], [1, 2]] - assert_raises(ValueError, random.multivariate_normal, mean, cov, - check_valid='raise') + actual = random.multivariate_normal(mean, cov) + desired = np.array([0.233278563284287, 9.424140804347195]) + assert_array_almost_equal(actual, desired, decimal=15) # Check that non positive-semidefinite covariance warns with # RuntimeWarning + mean = [0, 0] cov = [[1, 2], [2, 1]] - assert_warns(RuntimeWarning, random.multivariate_normal.__call__, mean, cov) - assert_warns(RuntimeWarning, random.multivariate_normal.__call__, mean, cov, - method='eigh') - assert_raises(LinAlgError, random.multivariate_normal, mean, cov, - method='cholesky') + assert_warns(RuntimeWarning, random.multivariate_normal, mean, cov) # and that it doesn't warn with RuntimeWarning check_valid='ignore' - assert_no_warnings(random.multivariate_normal.__call__, mean, cov, + assert_no_warnings(random.multivariate_normal, mean, cov, check_valid='ignore') # and that it raises with RuntimeWarning check_valid='raises' assert_raises(ValueError, random.multivariate_normal, mean, cov, check_valid='raise') - assert_raises(ValueError, random.multivariate_normal, mean, cov, - check_valid='raise', method='eigh') cov = np.array([[1, 0.1], [0.1, 1]], dtype=np.float32) with suppress_warnings() as sup: random.multivariate_normal(mean, cov) - random.multivariate_normal(mean, cov, method='eigh') - random.multivariate_normal(mean, cov, method='cholesky') w = sup.record(RuntimeWarning) assert len(w) == 0 |