Source code for toqito.rand.random_state_vector
"""Generates a random state vector."""
import numpy as np
from toqito.perms import swap
from toqito.states import max_entangled
[docs]
def random_state_vector(
dim: list[int] | tuple[int, ...] | int,
is_real: bool = False,
k_param: int = 0,
seed: int | None = None,
) -> np.ndarray:
r"""Generate a random pure state vector.
Randomness model
We sample entries independently from the standard normal distribution using ``numpy``'s
``default_rng``. If ``is_real`` is ``False`` (default), the imaginary part is sampled in the
same way and added with the factor \(i\); otherwise the vector is real. The sampled vector
is normalized to have unit Euclidean norm. When ``k_param`` is strictly positive, the returned
state describes a bipartite system of dimensions ``dim`` (or ``[dim, dim]`` if ``dim`` is an
integer) with Schmidt rank at most ``k_param``. This is achieved by drawing local factors and
combining them with a maximally entangled resource state.
Examples:
We may generate a random state vector. For instance, here is an example where we can generate a
\(2\)-dimensional random state vector.
```python exec="1" source="above" session="vec_example"
from toqito.rand import random_state_vector
vec = random_state_vector(2)
print(vec)
```
We can verify that this is in fact a valid state vector by computing the corresponding density
matrix of the vector and checking if the density matrix is pure.
```python exec="1" source="above" session="vec_example"
from toqito.state_props import is_pure
dm = vec @ vec.conj().T
print(is_pure(dm))
```
It is also possible to pass a seed for reproducibility.
```python exec="1" source="above" session="vec_example"
from toqito.rand import random_state_vector
vec = random_state_vector(2, seed=42)
print(vec)
```
We can once again verify that this is in fact a valid state vector by computing the
corresponding density matrix of the vector and checking if the density matrix is pure.
```python exec="1" source="above" session="vec_example"
from toqito.state_props import is_pure
dm = vec @ vec.conj().T
print(is_pure(dm))
```
Args:
dim: Either a positive integer giving the total Hilbert-space dimension, or a length-2 sequence specifying the
individual subsystem dimensions for bipartite sampling.
is_real: Boolean denoting whether the returned vector has real entries. Default is `False`, which produces
complex amplitudes.
k_param: Optional upper bound on the Schmidt rank when ``dim`` describes a bipartite system. Set to `0`
(default) to ignore the Schmidt rank constraint. Must be non-negative and strictly less than the smaller
subsystem dimension when used.
seed: A seed used to instantiate numpy's random number generator.
Returns:
A normalized column vector of shape ``(total_dim, 1)`` where ``total_dim`` equals `dim` if ``dim`` is an integer
and equals the product of entries in ``dim`` otherwise.
"""
gen = np.random.default_rng(seed=seed)
if k_param < 0:
msg = "k_param must be non-negative."
raise ValueError(msg)
if isinstance(dim, int):
dims_seq: list[int] | None = None
min_dim = dim
total_dim = dim
else:
dims_seq = list(dim)
if len(dims_seq) == 0:
msg = "dim must not be empty when provided as a sequence."
raise ValueError(msg)
if not all(isinstance(val, int) and val > 0 for val in dims_seq):
msg = "dim entries must be positive integers."
raise ValueError(msg)
min_dim = min(dims_seq)
total_dim = int(np.prod(dims_seq))
if 0 < k_param < min_dim:
if isinstance(dim, int):
dims_pair = [dim, dim]
else:
if len(dims_seq) != 2:
msg = "When k_param > 0, dim must be an integer or a length-2 sequence."
raise ValueError(msg)
dims_pair = dims_seq
psi = max_entangled(k_param, True, False).toarray()
a_param = gen.random((dims_pair[0] * k_param, 1))
b_param = gen.random((dims_pair[1] * k_param, 1))
if not is_real:
a_param = a_param + 1j * gen.random((dims_pair[0] * k_param, 1))
b_param = b_param + 1j * gen.random((dims_pair[1] * k_param, 1))
mat_1 = np.kron(psi.conj().T, np.identity(int(np.prod(dims_pair))))
mat_2 = swap(
np.kron(a_param, b_param),
sys=[2, 3],
dim=[k_param, dims_pair[0], k_param, dims_pair[1]],
)
ret_vec = mat_1 @ mat_2
ret_vec = ret_vec.reshape(-1, 1)
return np.divide(ret_vec, np.linalg.norm(ret_vec))
ret_vec = gen.random((total_dim, 1))
if not is_real:
ret_vec = ret_vec + 1j * gen.random((total_dim, 1))
return np.divide(ret_vec, np.linalg.norm(ret_vec))