Repository: Qiskit/qiskit

Issue:
Wrong probability distribution for random_statevector()
Note: It is not the case that the output isn't random enough, it's just that the distribution isn't what the description claims it is.

In qiskit-terra/qiskit/quantum_info/states/random.py, random_statevector() is defined as 
```
def random_statevector(dims, seed=None):
    """Generator a random Statevector.
    The statevector is sampled from the uniform (Haar) measure.
    Args:
        dims (int or tuple): the dimensions of the state.
        seed (int or np.random.Generator): Optional. Set a fixed seed or
                                           generator for RNG.
    Returns:
        Statevector: the random statevector.
    """
    if seed is None:
        rng = np.random.default_rng()
    elif isinstance(seed, np.random.Generator):
        rng = seed
    else:
        rng = default_rng(seed)

    dim = np.product(dims)

    # Random array over interval (0, 1]
    x = rng.random(dim)
    x += x == 0
    x = -np.log(x)
    sumx = sum(x)
    phases = rng.random(dim) * 2.0 * np.pi
    return Statevector(np.sqrt(x / sumx) * np.exp(1j * phases), dims=dims)
```

However, this doesn't seem to be Haar random for single qubit states. 

For higher dimensions, it's a simple computation to just check that $X_1,X_2\dots X_n$ all i.i.d distributed as $\pm \sqrt{-\log A}$ for $A$ distributed uniformly over $(0,1]$ don't lead to a spherically symmetric joint pdf for any $n$. Showing that normalization also does not lead to spherical symmetry might be hard for general $n$, but I have a way of showing it for single qubit states and here's the reasoning:

We'll show that the distribution of $|X|^2$  (where $X$ is the random variable for first coordinate of the output statevector) doesn't match up with that of $\cos^2 \theta$ for $\theta$ distributed uniformly over $\sim (0,2\pi)$.

Since we're looking only at the amplitude, it suffices to just see the distribution of 
$$Y= \frac {-\log A} {-\log A-\log B} $$

where $A,B$ are i.i.d random variables distributed uniformly over $(0,1]$. Let $f,g$ be the pdf for $Y,(-\log A)$ respectively. Then, we have

$$ \Pr[-\log A \leq t]= \Pr[A\geq \exp(-t) ]=1-\exp(-t)$$

$$\implies g(t)=\frac {d}{dt} (1-\exp(-t))=\exp(-t)  $$

$$\begin{align}
\Pr[Y\leq t] 
&=\int_{0}^\infty \Pr[ Y\leq t |-\log B=\theta] \cdot f(\theta) d\theta \\
&=\int_{0}^\infty \Pr\left[ \frac{-\log A}{-\log A+\theta}\leq t \right] \cdot \exp(-\theta) d\theta\\
&=\int_{0}^\infty \Pr\left[ -\log A \leq \frac{t\theta}{1-t}\right] \cdot \exp(-\theta) d\theta\\
&=\int_{0}^\infty (1-\exp(\frac {-t\theta}{1-t})) \cdot \exp(-\theta) d\theta\\
&=1-\int_{0}^\infty   \exp(-\frac {\theta}{1-t}) d\theta\\
&=1-(1-t)=t\\
\implies f(t)=\frac {dt}{dt}=1
\end{align}$$

So we have that $|X|^2$ is uniformly distributed over $(0,1)$, but this isn't the case for 1 qubit states as its distributed as $\cos^2\theta)$ , which is not uniform. (this computation probably can be extended to higher dimensions too).

To fix this, instead of $\sqrt{-\log}$, it might be better to sample from $N(0,1)$ as the standard multivariate normal is spherically symmetric.  Numpy does have an implementation for this too.



Discussion:

Resolving patch:
diff --git a/qiskit/quantum_info/states/random.py b/qiskit/quantum_info/states/random.py
--- a/qiskit/quantum_info/states/random.py
+++ b/qiskit/quantum_info/states/random.py
@@ -31,7 +31,8 @@ def random_statevector(
 ) -> Statevector:
     """Generator a random Statevector.
 
-    The statevector is sampled from the uniform (Haar) measure.
+    The statevector is sampled from the uniform distribution. This is the measure
+    induced by the Haar measure on unitary matrices.
 
     Args:
         dims (int or tuple): the dimensions of the state.
@@ -40,6 +41,10 @@ def random_statevector(
 
     Returns:
         Statevector: the random statevector.
+
+    Reference:
+        K. Zyczkowski and H. Sommers (2001), "Induced measures in the space of mixed quantum states",
+        `J. Phys. A: Math. Gen. 34 7111 <https://arxiv.org/abs/quant-ph/0012101>`__.
     """
     if seed is None:
         rng = np.random.default_rng()
@@ -49,14 +54,10 @@ def random_statevector(
         rng = default_rng(seed)
 
     dim = np.prod(dims)
-
-    # Random array over interval (0, 1]
-    x = rng.random(dim)
-    x += x == 0
-    x = -np.log(x)
-    sumx = sum(x)
-    phases = rng.random(dim) * 2.0 * np.pi
-    return Statevector(np.sqrt(x / sumx) * np.exp(1j * phases), dims=dims)
+    vec = rng.standard_normal(dim).astype(complex)
+    vec += 1j * rng.standard_normal(dim)
+    vec /= np.linalg.norm(vec)
+    return Statevector(vec, dims=dims)
 
 
 def random_density_matrix(


Test patch: