# 3. Built-in & Random States & Operators¶

## 3.2. Operators¶

Gate operators:

Hamiltonians and related operators:

Most of these are cached (and immutable), so can be called repeatedly without creating any new objects:

>>> pauli('Z') is pauli('Z')
True


## 3.3. Random States & Operators¶

Random pure states:

Random operators:

All of these functions accept a seed argument for replicability:

>>> rand_rho(2, seed=42)
qarray([[ 0.196764+7.758223e-19j, -0.08442 +2.133635e-01j],
[-0.08442 -2.133635e-01j,  0.803236-2.691589e-18j]])

>>> rand_rho(2, seed=42)
qarray([[ 0.196764+7.758223e-19j, -0.08442 +2.133635e-01j],
[-0.08442 -2.133635e-01j,  0.803236-2.691589e-18j]])


For some applications, generating random numbers with numpy alone can be a bottleneck. quimb will instead peform fast, multi-threaded random number generation with randomgen if it is installed, which can potentially offer an order of magnitude better performance. While the random number sequences can be still replicated using the seed argument, they also depend (deterministically) on the number of threads used, so may vary across machines unless this is set (e.g. with 'OMP_NUM_THREADS'). Use of randomgen can be explicitly turned off with the environment variable QUIMB_USE_RANDOMGEN='false'.

The following gives a quick idea of the speed-ups possible. First random, complex, normally distributed array generation with a naive numpy method:

>>> import numpy as np
>>> %timeit np.random.randn(2**22) + 1j * np.random.randn(2**22)
297 ms ± 2.09 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)


And generation with quimb:

>>> import quimb as qu
>>> %timeit qu.randn(2**22, dtype=complex)
32.1 ms ± 1.39 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)