summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRedRuM <44142765+zoj613@users.noreply.github.com>2019-08-17 17:50:56 +0200
committerRedRuM <44142765+zoj613@users.noreply.github.com>2019-08-17 17:50:56 +0200
commite6f9a354ff3e2354d343493f6463328895f702c2 (patch)
tree07db57859b734fab6b49e034d2ec9501fa86111a
parent31de73ba60e7128aced80e65c8e28b28e06bc7f6 (diff)
downloadnumpy-e6f9a354ff3e2354d343493f6463328895f702c2.tar.gz
REV: Undo all changes to codebase
-rw-r--r--numpy/random/generator.pyx164
-rw-r--r--numpy/random/tests/test_generator_mt19937.py58
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