Generating random vectors of Euclidean norm <= 1 in Python? - python

More specifically, given a natural number d, how can I generate random vectors in R^d such that each vector x has Euclidean norm <= 1?
Generating random vectors via numpy.random.rand(1,d) is no problem, but the likelihood of such a random vector having norm <= 1 is predictably bad for even not-small d. For example, even for d = 10 about 0.2% percent of such random vectors have appropriately small norm. So that seems like a silly solution.
EDIT: Re: Walter's comment, yes, I'm looking for a uniform distribution over vectors in the unit ball in R^d.

Based on the Wolfram Mathworld article on hypersphere point picking and Nate Eldredge's answer to a similar question on math.stackexchange.com, you can generate such a vector by generating a vector of d independent Gaussian random variables and a random number U uniformly distributed over the closed interval [0, 1], then normalizing the vector to norm U^(1/d).

Based on the answer by user2357112, you need something like this:
import numpy as np
...
inv_d = 1.0 / d
for ...:
gauss = np.random.normal(size=d)
length = np.linalg.norm(gauss)
if length == 0.0:
x = gauss
else:
r = np.random.rand() ** inv_d
x = np.multiply(gauss, r / length)
# conceptually: / length followed by * r
# do something with x
(this is my second Python program, so don't shoot at me...)
The tricks are that
the combination of d independent gaussian variables with same σ is a gaussian distribution in d dimensions, which, remarkably, has spherical symmetry,
the gaussian distribution in d dimensions can be projected onto the unit sphere by dividing by the norm, and
the uniform distribution in a d-dimensional unit sphere has cumulative radial distribution rd (which is what you need to invert)

this is the Python / Numpy code I am using. Since it does not use loops, is much faster:
n_vectors=1000
d=2
rnd_vec=np.random.uniform(-1, 1, size=(n_vectors, d)) # the initial random vectors
unif=np.random.uniform(size=n_vectors) # a second array random numbers
scale_f=np.expand_dims(np.linalg.norm(rnd_vec, axis=1)/unif, axis=1) # the scaling factors
rnd_vec=rnd_vec/scale_f # the random vectors in R^d
The second array of random numbers (unif) is needed as second scaling factor because otherwise all the vectors will have euclidean norm equal to one.

Related

how to generate uniform random complex numbers in python

For my research, I need to generate uniform random complex numbers. How to do this in python because there is no such module to generate the complex numbers.
Your question is underspecified, you need to say from what region of the complex plane you want to draw your uniformly distributed numbers.
For uniformly sampling real numbers, this is true as well.
However, in the real case there is a very obvious choice, namely the interval [0, 1).
You can see, for example that numpy.random.uniform per default samples from this interval.
I will present some solution for regions of the complex plane that could be useful, but ultimately, the choice that is right for you will depend on your application.
Assume np is numpy and that we want to genereate an array of many such random numbers with shape shape.
A square centered at the origin
I.e. sampling uniformly from all complex numbers z such that both real and imaginary part are in [-1,1]. You can generate such complex numbers e.g. via
np.random.uniform(-1, 1, shape) + 1.j * np.random.uniform(-1, 1, shape)
A disc centered at the origin
I.e. sampling uniformly from all complex numbers with absolute value in [0,1]. You can generate them e.g. as
np.sqrt(np.random.uniform(0, 1, shape)) * np.exp(1.j * np.random.uniform(0, 2 * np.pi, shape))
Explanation: We can parametrize points in the disc as z = r * exp(i a).
Uniform distribution of z in the disc means that the angle a is uniform in [0, 2pi] but the radius is non-uniform (intuition: in the disc there are more points with larger radius than with a small one).
the radius has a probability density of p(r) = 2r on the interval [0, 1], and a CDF (integral of p(r)) of F(r) = r^2, inverse CDF sampling then allows us to sample such radii as X = F^{-1}(Y) = sqrt(Y) where Y is uniformly distributed.
Is it not enough to do:
a = np.random.uniform(1,10,10)
b = a + a * <some constant>j
I think this stays uniform.
array([7.51553061 +9.01863673j, 1.53844779 +1.84613735j,
2.33666459 +2.80399751j, 9.44081138+11.32897366j,
7.47316887 +8.96780264j, 6.96193206 +8.35431847j,
9.13933486+10.96720183j, 2.10023098 +2.52027718j,
4.70705458 +5.6484655j , 8.02055689 +9.62466827j])

Lanczos algorithm for finding top eigenvalues of a matrix sum

I am trying to find the top k leading eigenvalues of a numpy matrix (using python dot product notation)
L#L + a*Y#Y.T, where L and Y are a symmetric nxn and an nxd matrix, respectively.
According to the below text from this paper, I should be able to calculate these leading eigenvalues with L#(L#v) + a*X#(X.T#v), where I guess v is an arbitrary vector. The Lanczos paper they cite is here.
I'm not quite sure where to start. I know that scipy has scipy.sparse.linalg.eigsh here, and from the notes it looks like it uses the Lanczos algorithm - but I am at a loss as to whether it's possible to use sparse.linalg.eigsh for my specific use case. I googled around and didn't find a Python implementation for this very quickly -- does anybody know if I can use sparse.linalg.eigsh to calculate this somehow? I definitely don't want to write this algorithm myself.
I also wasn't sure whether to post this in math.stackexchange or here, since it's a question about the Python implementation of a very mathy thing.
You could check scipy.sparse.linalg.eigsh.
import numpy as np;
from scipy.sparse.linalg import eigsh;
from numpy.linalg import eigh
a = 1.4
n = 20;
d = 7;
# random symmetric n x n matrix
L = np.random.randn(n, n)
L = L + L.T
# random n x d matrix
Y = np.random.randn(n, d)
A = L # L.T + a * Y # Y.T # your equation
A must be positive-definite to use eigsh, this is guaranteed to be true if a>0.
You could check the four eigenvalues as follows
eigsh(La, 4)[0]
For reference you can compare based on numpy.linalg.eigh that compute all the eigenvalues. Sort them, and take the last four elements of the sorted array, the results should be close.
np.sort(eigh(La)[0])[-4:]

Functionality of rnorm with numpy.random.normal

For some vector m (of length N) of numbers in R we can write
rnorm(N, mean = m, sd = 1)
and this will give a vector of length N where each element will be a sample for a normal distribution centred at the different elements of m. My question is, is it possible to do the same easily with numpy? As far as I can tell numpy.random.normal() requires the loc to be the same for all the elements. The point is that I want a random vector with different means.
Also while writing this, would it work to sample from a standard normal distribution and transform this sample? That would be easier.
One way you can do is random sampling at center 0 then move the sample:
m, N = np.array([1,2,3]), 1000
np.random.seed(42)
samples = np.random.randn(N,len(m)) + m

Generating a vector with with a random uniform direction and a Gaussian-distributed magnitude

I want to add 2D Gaussian noise to each (x,y) point of a list that I have.
That is why I want to create a noise vector with a random uniform direction over [0, 2pi) and a Gaussian-distributed magnitude with N(0, \sigma^2).
How can I generate a vector in Python only specifying the direction and its magnitude?
Well, this is not hard to do
n = 100
sigma = 1.0
phi = 2.0 * np.pi * np.random.random(n)
r = np.random.normal(loc=0.0, scale=sigma, size=n)
x = r*np.cos(phi)
y = r*np.sin(phi)
You can generate two vectors, one for the magnitude and another for the phase. Then you use both to get what you want.
import numpy as np
import math
sigma_squred = 0.01 # Change to whatever value you want
num_elements = 10 # Size of the vector you want
magnitude = math.sqrt(sigma_squred) * np.random.randn(num_elements)
phase = 2 * np.pi * np.random.random_sample(num_elements)
# This will give you a vector with a Gaussian magnitude and a random phase between 0 and 2PI
noise = magnitude * np.exp(1j*phase)
I find it easier to work with a single vector of complex numbers, but since you have individual x and y values, you can get a noise_x and a noise_y vector with
noise_x = noise.real
noise_y = noise.imag
Note: I'm assuming you can use the numpy library, which make things much easier. If that is not the case you will need a loop to generate each element. To generate a single sample for magnitude you can use random.gauss(0, sigma), while 2*math.pi*random.random() can be used to generate a sample for the phase. Then you do the same as before to get a complex number from where you can get the real and the imaginary parts.

non-uniform distributed random array

I need to generate a vector of random float numbers between [0,1] such
that their sum equals 1 and that are distributed non-uniformly.
Is there any python function that generates such a vector?
Best wishes
The distribution you are probably looking for is called the Dirichlet distribution. There's no built-in function in Python for drawing random numbers from a Dirichlet distribution, but NumPy contains one:
>>> from numpy.random.mtrand import dirichlet
>>> print dirichlet([1] * n)
This will give you n numbers that sum up to 1, and the probability of each such combination will be equal.
Alternatively, if you don't have NumPy, you can make use of the fact that a random sample drawn from an n-dimensional Dirichlet distribution can be generated by drawing n independent samples from a gamma distribution with shape and scale parameters equal to 1 and then dividing the samples with the sum:
>>> from random import gammavariate
>>> def dirichlet(n):
... samples = [gammavariate(1, 1) for _ in xrange(n)]
... sum_samples = sum(samples)
... return [x/sum_samples for x in samples]
The reason why you need a Dirichlet distribution is because if you simply draw random numbers uniformly from some interval and then divide them by the sum of them, the resulting distribution will be biased towards samples consisting of roughly equal numbers. See Luc Devroye's book for more on this topic.
There is a nicer example in Wikipedia page: Dirichlet distribution.
The code below generate a k dimension sample:
params = [a1, a2, ..., ak]
sample = [random.gammavariate(a,1) for a in params]
sample = [v/sum(sample) for v in sample]

Categories

Resources