Trajectree 0.0.0__py3-none-any.whl → 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- trajectree/__init__.py +3 -0
- trajectree/fock_optics/devices.py +1 -1
- trajectree/fock_optics/light_sources.py +2 -2
- trajectree/fock_optics/measurement.py +3 -3
- trajectree/fock_optics/utils.py +6 -6
- trajectree/quimb/docs/_pygments/_pygments_dark.py +118 -0
- trajectree/quimb/docs/_pygments/_pygments_light.py +118 -0
- trajectree/quimb/docs/conf.py +158 -0
- trajectree/quimb/docs/examples/ex_mpi_expm_evo.py +62 -0
- trajectree/quimb/quimb/__init__.py +507 -0
- trajectree/quimb/quimb/calc.py +1491 -0
- trajectree/quimb/quimb/core.py +2279 -0
- trajectree/quimb/quimb/evo.py +712 -0
- trajectree/quimb/quimb/experimental/__init__.py +0 -0
- trajectree/quimb/quimb/experimental/autojittn.py +129 -0
- trajectree/quimb/quimb/experimental/belief_propagation/__init__.py +109 -0
- trajectree/quimb/quimb/experimental/belief_propagation/bp_common.py +397 -0
- trajectree/quimb/quimb/experimental/belief_propagation/d1bp.py +316 -0
- trajectree/quimb/quimb/experimental/belief_propagation/d2bp.py +653 -0
- trajectree/quimb/quimb/experimental/belief_propagation/hd1bp.py +571 -0
- trajectree/quimb/quimb/experimental/belief_propagation/hv1bp.py +775 -0
- trajectree/quimb/quimb/experimental/belief_propagation/l1bp.py +316 -0
- trajectree/quimb/quimb/experimental/belief_propagation/l2bp.py +537 -0
- trajectree/quimb/quimb/experimental/belief_propagation/regions.py +194 -0
- trajectree/quimb/quimb/experimental/cluster_update.py +286 -0
- trajectree/quimb/quimb/experimental/merabuilder.py +865 -0
- trajectree/quimb/quimb/experimental/operatorbuilder/__init__.py +15 -0
- trajectree/quimb/quimb/experimental/operatorbuilder/operatorbuilder.py +1631 -0
- trajectree/quimb/quimb/experimental/schematic.py +7 -0
- trajectree/quimb/quimb/experimental/tn_marginals.py +130 -0
- trajectree/quimb/quimb/experimental/tnvmc.py +1483 -0
- trajectree/quimb/quimb/gates.py +36 -0
- trajectree/quimb/quimb/gen/__init__.py +2 -0
- trajectree/quimb/quimb/gen/operators.py +1167 -0
- trajectree/quimb/quimb/gen/rand.py +713 -0
- trajectree/quimb/quimb/gen/states.py +479 -0
- trajectree/quimb/quimb/linalg/__init__.py +6 -0
- trajectree/quimb/quimb/linalg/approx_spectral.py +1109 -0
- trajectree/quimb/quimb/linalg/autoblock.py +258 -0
- trajectree/quimb/quimb/linalg/base_linalg.py +719 -0
- trajectree/quimb/quimb/linalg/mpi_launcher.py +397 -0
- trajectree/quimb/quimb/linalg/numpy_linalg.py +244 -0
- trajectree/quimb/quimb/linalg/rand_linalg.py +514 -0
- trajectree/quimb/quimb/linalg/scipy_linalg.py +293 -0
- trajectree/quimb/quimb/linalg/slepc_linalg.py +892 -0
- trajectree/quimb/quimb/schematic.py +1518 -0
- trajectree/quimb/quimb/tensor/__init__.py +401 -0
- trajectree/quimb/quimb/tensor/array_ops.py +610 -0
- trajectree/quimb/quimb/tensor/circuit.py +4824 -0
- trajectree/quimb/quimb/tensor/circuit_gen.py +411 -0
- trajectree/quimb/quimb/tensor/contraction.py +336 -0
- trajectree/quimb/quimb/tensor/decomp.py +1255 -0
- trajectree/quimb/quimb/tensor/drawing.py +1646 -0
- trajectree/quimb/quimb/tensor/fitting.py +385 -0
- trajectree/quimb/quimb/tensor/geometry.py +583 -0
- trajectree/quimb/quimb/tensor/interface.py +114 -0
- trajectree/quimb/quimb/tensor/networking.py +1058 -0
- trajectree/quimb/quimb/tensor/optimize.py +1818 -0
- trajectree/quimb/quimb/tensor/tensor_1d.py +4778 -0
- trajectree/quimb/quimb/tensor/tensor_1d_compress.py +1854 -0
- trajectree/quimb/quimb/tensor/tensor_1d_tebd.py +662 -0
- trajectree/quimb/quimb/tensor/tensor_2d.py +5954 -0
- trajectree/quimb/quimb/tensor/tensor_2d_compress.py +96 -0
- trajectree/quimb/quimb/tensor/tensor_2d_tebd.py +1230 -0
- trajectree/quimb/quimb/tensor/tensor_3d.py +2869 -0
- trajectree/quimb/quimb/tensor/tensor_3d_tebd.py +46 -0
- trajectree/quimb/quimb/tensor/tensor_approx_spectral.py +60 -0
- trajectree/quimb/quimb/tensor/tensor_arbgeom.py +3237 -0
- trajectree/quimb/quimb/tensor/tensor_arbgeom_compress.py +565 -0
- trajectree/quimb/quimb/tensor/tensor_arbgeom_tebd.py +1138 -0
- trajectree/quimb/quimb/tensor/tensor_builder.py +5411 -0
- trajectree/quimb/quimb/tensor/tensor_core.py +11179 -0
- trajectree/quimb/quimb/tensor/tensor_dmrg.py +1472 -0
- trajectree/quimb/quimb/tensor/tensor_mera.py +204 -0
- trajectree/quimb/quimb/utils.py +892 -0
- trajectree/quimb/tests/__init__.py +0 -0
- trajectree/quimb/tests/test_accel.py +501 -0
- trajectree/quimb/tests/test_calc.py +788 -0
- trajectree/quimb/tests/test_core.py +847 -0
- trajectree/quimb/tests/test_evo.py +565 -0
- trajectree/quimb/tests/test_gen/__init__.py +0 -0
- trajectree/quimb/tests/test_gen/test_operators.py +361 -0
- trajectree/quimb/tests/test_gen/test_rand.py +296 -0
- trajectree/quimb/tests/test_gen/test_states.py +261 -0
- trajectree/quimb/tests/test_linalg/__init__.py +0 -0
- trajectree/quimb/tests/test_linalg/test_approx_spectral.py +368 -0
- trajectree/quimb/tests/test_linalg/test_base_linalg.py +351 -0
- trajectree/quimb/tests/test_linalg/test_mpi_linalg.py +127 -0
- trajectree/quimb/tests/test_linalg/test_numpy_linalg.py +84 -0
- trajectree/quimb/tests/test_linalg/test_rand_linalg.py +134 -0
- trajectree/quimb/tests/test_linalg/test_slepc_linalg.py +283 -0
- trajectree/quimb/tests/test_tensor/__init__.py +0 -0
- trajectree/quimb/tests/test_tensor/test_belief_propagation/__init__.py +0 -0
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_d1bp.py +39 -0
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_d2bp.py +67 -0
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_hd1bp.py +64 -0
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_hv1bp.py +51 -0
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_l1bp.py +142 -0
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_l2bp.py +101 -0
- trajectree/quimb/tests/test_tensor/test_circuit.py +816 -0
- trajectree/quimb/tests/test_tensor/test_contract.py +67 -0
- trajectree/quimb/tests/test_tensor/test_decomp.py +40 -0
- trajectree/quimb/tests/test_tensor/test_mera.py +52 -0
- trajectree/quimb/tests/test_tensor/test_optimizers.py +488 -0
- trajectree/quimb/tests/test_tensor/test_tensor_1d.py +1171 -0
- trajectree/quimb/tests/test_tensor/test_tensor_2d.py +606 -0
- trajectree/quimb/tests/test_tensor/test_tensor_2d_tebd.py +144 -0
- trajectree/quimb/tests/test_tensor/test_tensor_3d.py +123 -0
- trajectree/quimb/tests/test_tensor/test_tensor_arbgeom.py +226 -0
- trajectree/quimb/tests/test_tensor/test_tensor_builder.py +441 -0
- trajectree/quimb/tests/test_tensor/test_tensor_core.py +2066 -0
- trajectree/quimb/tests/test_tensor/test_tensor_dmrg.py +388 -0
- trajectree/quimb/tests/test_tensor/test_tensor_spectral_approx.py +63 -0
- trajectree/quimb/tests/test_tensor/test_tensor_tebd.py +270 -0
- trajectree/quimb/tests/test_utils.py +85 -0
- trajectree/trajectory.py +2 -2
- {trajectree-0.0.0.dist-info → trajectree-0.0.1.dist-info}/METADATA +2 -2
- trajectree-0.0.1.dist-info/RECORD +126 -0
- trajectree-0.0.0.dist-info/RECORD +0 -16
- {trajectree-0.0.0.dist-info → trajectree-0.0.1.dist-info}/WHEEL +0 -0
- {trajectree-0.0.0.dist-info → trajectree-0.0.1.dist-info}/licenses/LICENSE +0 -0
- {trajectree-0.0.0.dist-info → trajectree-0.0.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,713 @@
|
|
|
1
|
+
"""Functions for generating random quantum objects and states."""
|
|
2
|
+
|
|
3
|
+
import math
|
|
4
|
+
import random
|
|
5
|
+
from functools import wraps
|
|
6
|
+
from numbers import Integral
|
|
7
|
+
from itertools import count, chain
|
|
8
|
+
from concurrent.futures import wait
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
import numpy.random
|
|
12
|
+
import scipy.sparse as sp
|
|
13
|
+
|
|
14
|
+
from ..core import (
|
|
15
|
+
_NUM_THREAD_WORKERS,
|
|
16
|
+
complex_array,
|
|
17
|
+
dag,
|
|
18
|
+
dot,
|
|
19
|
+
get_thread_pool,
|
|
20
|
+
kron,
|
|
21
|
+
nmlz,
|
|
22
|
+
prod,
|
|
23
|
+
ptr,
|
|
24
|
+
pvectorize,
|
|
25
|
+
qarray,
|
|
26
|
+
qu,
|
|
27
|
+
rdmul,
|
|
28
|
+
vectorize,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class _RGenHandler:
|
|
33
|
+
"""Private object that handles pool of random number generators for
|
|
34
|
+
parallel number generation - seeding them & changing the underlying bit
|
|
35
|
+
generators.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(self, initial_seed=None, initial_bitgen=None):
|
|
39
|
+
self.rgs = []
|
|
40
|
+
self.set_seed(initial_seed)
|
|
41
|
+
self.set_bitgen(initial_bitgen)
|
|
42
|
+
|
|
43
|
+
def set_bitgen(self, bitgen):
|
|
44
|
+
"""Set the core underlying bit-generator.
|
|
45
|
+
|
|
46
|
+
Parameters
|
|
47
|
+
----------
|
|
48
|
+
bitgen : {None, str}
|
|
49
|
+
Which bit generator to use, either from numpy or `randomgen` -
|
|
50
|
+
https://bashtage.github.io/randomgen/bit_generators/index.html.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
if bitgen is None:
|
|
54
|
+
self.gen_fn = numpy.random.default_rng
|
|
55
|
+
|
|
56
|
+
else:
|
|
57
|
+
try:
|
|
58
|
+
# try numpy first
|
|
59
|
+
bg = getattr(numpy.random, bitgen)
|
|
60
|
+
except AttributeError:
|
|
61
|
+
# and then (now largely deprecated) randomgen
|
|
62
|
+
import randomgen
|
|
63
|
+
|
|
64
|
+
bg = getattr(randomgen, bitgen)
|
|
65
|
+
|
|
66
|
+
def gen(s):
|
|
67
|
+
return numpy.random.Generator(bg(s))
|
|
68
|
+
|
|
69
|
+
self.gen_fn = gen
|
|
70
|
+
|
|
71
|
+
# delete any old rgens
|
|
72
|
+
self.rgs = []
|
|
73
|
+
|
|
74
|
+
def set_seed(self, seed=None):
|
|
75
|
+
"""Set the seed for the bit generators.
|
|
76
|
+
|
|
77
|
+
Parameters
|
|
78
|
+
----------
|
|
79
|
+
seed : {None, int}, optional
|
|
80
|
+
Seed supplied to `numpy.random.SeedSequence`. None will randomly
|
|
81
|
+
the generators (default).
|
|
82
|
+
"""
|
|
83
|
+
seq = numpy.random.SeedSequence(seed)
|
|
84
|
+
|
|
85
|
+
# compute seeds in batches of 4 for perf
|
|
86
|
+
self.seeds = iter(chain.from_iterable(seq.spawn(4) for _ in count()))
|
|
87
|
+
|
|
88
|
+
# delete any old rgens
|
|
89
|
+
self.rgs = []
|
|
90
|
+
|
|
91
|
+
def get_rgens(self, num_threads):
|
|
92
|
+
"""Get a list of the :class:`numpy.random.Generator` instances, having
|
|
93
|
+
made sure there are at least ``num_threads``.
|
|
94
|
+
|
|
95
|
+
Parameters
|
|
96
|
+
----------
|
|
97
|
+
num_threads : int
|
|
98
|
+
The number of generators to return.
|
|
99
|
+
|
|
100
|
+
Returns
|
|
101
|
+
-------
|
|
102
|
+
list[numpy.random.Generator]
|
|
103
|
+
"""
|
|
104
|
+
num_gens = len(self.rgs)
|
|
105
|
+
|
|
106
|
+
if num_gens < num_threads:
|
|
107
|
+
self.rgs.extend(
|
|
108
|
+
self.gen_fn(next(self.seeds))
|
|
109
|
+
for _ in range(num_gens, num_threads)
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
return self.rgs[:num_threads]
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
_RG_HANDLER = _RGenHandler()
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def seed_rand(seed):
|
|
119
|
+
"""See the random number generators, by instantiating a new set of bit
|
|
120
|
+
generators with a 'seed sequence'.
|
|
121
|
+
"""
|
|
122
|
+
global _RG_HANDLER
|
|
123
|
+
return _RG_HANDLER.set_seed(seed)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def set_rand_bitgen(bitgen):
|
|
127
|
+
"""Set the core bit generator type to use, from either ``numpy`` or
|
|
128
|
+
``randomgen``.
|
|
129
|
+
|
|
130
|
+
Parameters
|
|
131
|
+
----------
|
|
132
|
+
bitgen : {'PCG64', 'SFC64', 'MT19937', 'Philox', str}
|
|
133
|
+
Which bit generator to use.
|
|
134
|
+
"""
|
|
135
|
+
global _RG_HANDLER
|
|
136
|
+
return _RG_HANDLER.set_bitgen(bitgen)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _get_rgens(num_threads):
|
|
140
|
+
global _RG_HANDLER
|
|
141
|
+
return _RG_HANDLER.get_rgens(num_threads)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def randn(
|
|
145
|
+
shape=(),
|
|
146
|
+
dtype=float,
|
|
147
|
+
scale=1.0,
|
|
148
|
+
loc=0.0,
|
|
149
|
+
num_threads=None,
|
|
150
|
+
seed=None,
|
|
151
|
+
dist="normal",
|
|
152
|
+
):
|
|
153
|
+
"""Fast multithreaded generation of random normally distributed data.
|
|
154
|
+
|
|
155
|
+
Parameters
|
|
156
|
+
----------
|
|
157
|
+
shape : tuple[int]
|
|
158
|
+
The shape of the output random array.
|
|
159
|
+
dtype : {'complex128', 'float64', 'complex64' 'float32'}, optional
|
|
160
|
+
The data-type of the output array.
|
|
161
|
+
scale : float, optional
|
|
162
|
+
A multiplicative scale for the random numbers.
|
|
163
|
+
loc : float, optional
|
|
164
|
+
An additive location for the random numbers.
|
|
165
|
+
num_threads : int, optional
|
|
166
|
+
How many threads to use. If ``None``, decide automatically.
|
|
167
|
+
dist : {'normal', 'uniform', 'rademacher', 'exp'}, optional
|
|
168
|
+
Type of random number to generate.
|
|
169
|
+
"""
|
|
170
|
+
if seed is not None:
|
|
171
|
+
seed_rand(seed)
|
|
172
|
+
|
|
173
|
+
if isinstance(shape, Integral):
|
|
174
|
+
d = shape
|
|
175
|
+
shape = (shape,)
|
|
176
|
+
else:
|
|
177
|
+
d = prod(shape)
|
|
178
|
+
|
|
179
|
+
if dist == "rademacher":
|
|
180
|
+
# not parallelized for now
|
|
181
|
+
return rand_rademacher(shape, scale=scale, loc=loc, dtype=dtype)
|
|
182
|
+
|
|
183
|
+
if num_threads is None:
|
|
184
|
+
# only multi-thread for big ``d``
|
|
185
|
+
if d <= 32768:
|
|
186
|
+
num_threads = 1
|
|
187
|
+
else:
|
|
188
|
+
num_threads = _NUM_THREAD_WORKERS
|
|
189
|
+
|
|
190
|
+
rgs = _get_rgens(num_threads)
|
|
191
|
+
|
|
192
|
+
gen_method = {
|
|
193
|
+
"uniform": "random",
|
|
194
|
+
"normal": "standard_normal",
|
|
195
|
+
"exp": "standard_exponential",
|
|
196
|
+
}.get(dist, dist)
|
|
197
|
+
|
|
198
|
+
# sequential generation
|
|
199
|
+
if num_threads <= 1:
|
|
200
|
+
|
|
201
|
+
def create(d, dtype):
|
|
202
|
+
out = np.empty(d, dtype)
|
|
203
|
+
getattr(rgs[0], gen_method)(out=out, dtype=dtype)
|
|
204
|
+
return out
|
|
205
|
+
|
|
206
|
+
# threaded generation
|
|
207
|
+
else:
|
|
208
|
+
pool = get_thread_pool()
|
|
209
|
+
S = math.ceil(d / num_threads)
|
|
210
|
+
|
|
211
|
+
def _fill(gen, out, dtype, first, last):
|
|
212
|
+
getattr(gen, gen_method)(out=out[first:last], dtype=dtype)
|
|
213
|
+
|
|
214
|
+
def create(d, dtype):
|
|
215
|
+
out = np.empty(d, dtype)
|
|
216
|
+
# submit thread work
|
|
217
|
+
fs = [
|
|
218
|
+
pool.submit(_fill, gen, out, dtype, i * S, (i + 1) * S)
|
|
219
|
+
for i, gen in enumerate(rgs)
|
|
220
|
+
]
|
|
221
|
+
wait(fs)
|
|
222
|
+
return out
|
|
223
|
+
|
|
224
|
+
if np.issubdtype(dtype, np.floating):
|
|
225
|
+
out = create(d, dtype)
|
|
226
|
+
|
|
227
|
+
elif np.issubdtype(dtype, np.complexfloating):
|
|
228
|
+
# need to sum two real arrays if generating complex numbers
|
|
229
|
+
if np.issubdtype(dtype, np.complex64):
|
|
230
|
+
sub_dtype = np.float32
|
|
231
|
+
else:
|
|
232
|
+
sub_dtype = np.float64
|
|
233
|
+
|
|
234
|
+
out = complex_array(create(d, sub_dtype), create(d, sub_dtype))
|
|
235
|
+
|
|
236
|
+
else:
|
|
237
|
+
raise ValueError(f"dtype {dtype} not understood.")
|
|
238
|
+
|
|
239
|
+
if out.dtype != dtype:
|
|
240
|
+
out = out.astype(dtype)
|
|
241
|
+
|
|
242
|
+
if scale != 1.0:
|
|
243
|
+
out *= scale
|
|
244
|
+
if loc != 0.0:
|
|
245
|
+
out += loc
|
|
246
|
+
|
|
247
|
+
return out.reshape(shape)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
@wraps(randn)
|
|
251
|
+
def rand(*args, **kwargs):
|
|
252
|
+
kwargs.setdefault("dist", "uniform")
|
|
253
|
+
return randn(*args, **kwargs)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def random_seed_fn(fn):
|
|
257
|
+
"""Modify ``fn`` to take a ``seed`` argument (so as to seed the random
|
|
258
|
+
generators once-only at beginning of function not every ``randn`` call).
|
|
259
|
+
"""
|
|
260
|
+
|
|
261
|
+
@wraps(fn)
|
|
262
|
+
def wrapped_fn(*args, seed=None, **kwargs):
|
|
263
|
+
if seed is not None:
|
|
264
|
+
seed_rand(seed)
|
|
265
|
+
return fn(*args, **kwargs)
|
|
266
|
+
|
|
267
|
+
return wrapped_fn
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def _randint(*args, **kwargs):
|
|
271
|
+
return _get_rgens(1)[0].integers(*args, **kwargs)
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def _choice(*args, **kwargs):
|
|
275
|
+
return _get_rgens(1)[0].choice(*args, **kwargs)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
choice = random_seed_fn(_choice)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
@random_seed_fn
|
|
282
|
+
def rand_rademacher(shape, scale=1, loc=0.0, dtype=float):
|
|
283
|
+
""" """
|
|
284
|
+
if np.issubdtype(dtype, np.floating):
|
|
285
|
+
entries = loc + np.array([1.0, -1.0]) * scale
|
|
286
|
+
need2convert = dtype not in (float, np.float64)
|
|
287
|
+
|
|
288
|
+
elif np.issubdtype(dtype, np.complexfloating):
|
|
289
|
+
entries = loc + np.array([1.0, -1.0, 1.0j, -1.0j]) * scale
|
|
290
|
+
need2convert = dtype not in (complex, np.complex128)
|
|
291
|
+
|
|
292
|
+
else:
|
|
293
|
+
raise TypeError(
|
|
294
|
+
f"dtype {dtype} not understood - should be float or complex."
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
x = _choice(entries, shape)
|
|
298
|
+
if need2convert:
|
|
299
|
+
x = x.astype(dtype)
|
|
300
|
+
|
|
301
|
+
return x
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def _phase_to_complex_base(x):
|
|
305
|
+
return 1j * math.sin(x) + math.cos(x)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
_phase_sigs = ["complex64(float32)", "complex128(float64)"]
|
|
309
|
+
_phase_to_complex_seq = vectorize(_phase_sigs)(_phase_to_complex_base)
|
|
310
|
+
"""Turn array of phases into unit circle complex numbers - sequential.
|
|
311
|
+
"""
|
|
312
|
+
_phase_to_complex_par = pvectorize(_phase_sigs)(_phase_to_complex_base)
|
|
313
|
+
"""Turn array of phases into unit circle complex numbers - parallel.
|
|
314
|
+
"""
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def phase_to_complex(x):
|
|
318
|
+
if x.size >= 512:
|
|
319
|
+
return _phase_to_complex_par(x)
|
|
320
|
+
# XXX: this is not as fast as numexpr - investigate?
|
|
321
|
+
return _phase_to_complex_seq(x)
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
@random_seed_fn
|
|
325
|
+
def rand_phase(shape, scale=1, dtype=complex):
|
|
326
|
+
"""Generate random complex numbers distributed on the unit sphere."""
|
|
327
|
+
if not np.issubdtype(dtype, np.complexfloating):
|
|
328
|
+
raise ValueError(f"dtype must be complex, got '{dtype}'.")
|
|
329
|
+
|
|
330
|
+
if np.issubdtype(dtype, np.complex64):
|
|
331
|
+
sub_dtype = np.float32
|
|
332
|
+
else:
|
|
333
|
+
sub_dtype = np.float64
|
|
334
|
+
|
|
335
|
+
phi = randn(shape, dtype=sub_dtype, scale=2 * math.pi, dist="uniform")
|
|
336
|
+
z = phase_to_complex(phi)
|
|
337
|
+
if scale != 1:
|
|
338
|
+
z *= scale
|
|
339
|
+
|
|
340
|
+
return z
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def get_rand_fill_fn(
|
|
344
|
+
dist="normal",
|
|
345
|
+
loc=0.0,
|
|
346
|
+
scale=1.0,
|
|
347
|
+
seed=None,
|
|
348
|
+
dtype="float64",
|
|
349
|
+
):
|
|
350
|
+
"""Get a callable with the given random distribution and parameters, that
|
|
351
|
+
has signature ``fill_fn(shape) -> array``.
|
|
352
|
+
|
|
353
|
+
Parameters
|
|
354
|
+
----------
|
|
355
|
+
dist : {'normal', 'uniform', 'rademacher', 'exp'}, optional
|
|
356
|
+
Type of random number to generate, defaults to 'normal'.
|
|
357
|
+
loc : float, optional
|
|
358
|
+
An additive offset to add to the random numbers.
|
|
359
|
+
scale : float, optional
|
|
360
|
+
A multiplicative factor to scale the random numbers by.
|
|
361
|
+
seed : int, optional
|
|
362
|
+
A random seed.
|
|
363
|
+
dtype : {'float64', 'complex128', 'float32', 'complex64'}, optional
|
|
364
|
+
The underlying data type.
|
|
365
|
+
|
|
366
|
+
Returns
|
|
367
|
+
-------
|
|
368
|
+
callable
|
|
369
|
+
"""
|
|
370
|
+
if seed is not None:
|
|
371
|
+
seed_rand(seed)
|
|
372
|
+
|
|
373
|
+
def fill_fn(shape=()):
|
|
374
|
+
return randn(shape, dtype=dtype, dist=dist, loc=loc, scale=scale)
|
|
375
|
+
|
|
376
|
+
return fill_fn
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def rand_matrix(
|
|
380
|
+
d,
|
|
381
|
+
scaled=True,
|
|
382
|
+
sparse=False,
|
|
383
|
+
stype="csr",
|
|
384
|
+
density=None,
|
|
385
|
+
dtype=complex,
|
|
386
|
+
seed=None,
|
|
387
|
+
):
|
|
388
|
+
"""Generate a random matrix of order `d` with normally distributed
|
|
389
|
+
entries. If `scaled` is `True`, then in the limit of large `d` the
|
|
390
|
+
eigenvalues will be distributed on the unit complex disk.
|
|
391
|
+
|
|
392
|
+
Parameters
|
|
393
|
+
----------
|
|
394
|
+
d : int
|
|
395
|
+
Matrix dimension.
|
|
396
|
+
scaled : bool, optional
|
|
397
|
+
Whether to scale the matrices values such that its spectrum
|
|
398
|
+
approximately lies on the unit disk (for dense matrices).
|
|
399
|
+
sparse : bool, optional
|
|
400
|
+
Whether to produce a sparse matrix.
|
|
401
|
+
stype : {'csr', 'csc', 'coo', ...}, optional
|
|
402
|
+
The type of sparse matrix if ``sparse=True``.
|
|
403
|
+
density : float, optional
|
|
404
|
+
Target density of non-zero elements for the sparse matrix. By default
|
|
405
|
+
aims for about 10 entries per row.
|
|
406
|
+
dtype : {complex, float}, optional
|
|
407
|
+
The data type of the matrix elements.
|
|
408
|
+
|
|
409
|
+
Returns
|
|
410
|
+
-------
|
|
411
|
+
mat : qarray or sparse matrix
|
|
412
|
+
Random matrix.
|
|
413
|
+
"""
|
|
414
|
+
if np.issubdtype(dtype, np.floating):
|
|
415
|
+
iscomplex = False
|
|
416
|
+
elif np.issubdtype(dtype, np.complexfloating):
|
|
417
|
+
iscomplex = True
|
|
418
|
+
else:
|
|
419
|
+
raise TypeError(
|
|
420
|
+
f"dtype {dtype} not understood - should be " "float or complex."
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
# handle seed manually since standard python random.seed might be called
|
|
424
|
+
if seed is not None:
|
|
425
|
+
seed_rand(seed)
|
|
426
|
+
|
|
427
|
+
if sparse:
|
|
428
|
+
# Aim for 10 non-zero values per row, but betwen 1 and d/2
|
|
429
|
+
density = min(10, d / 2) / d if density is None else density
|
|
430
|
+
density = min(
|
|
431
|
+
max(
|
|
432
|
+
d**-2,
|
|
433
|
+
density,
|
|
434
|
+
),
|
|
435
|
+
1.0,
|
|
436
|
+
)
|
|
437
|
+
nnz = round(density * d * d)
|
|
438
|
+
|
|
439
|
+
if density > 0.1:
|
|
440
|
+
# take special care to avoid duplicates
|
|
441
|
+
if seed is not None:
|
|
442
|
+
random.seed(seed)
|
|
443
|
+
ijs = random.sample(range(0, d**2), k=nnz)
|
|
444
|
+
else:
|
|
445
|
+
ijs = _randint(0, d * d, size=nnz)
|
|
446
|
+
|
|
447
|
+
# want to sample nnz unique (d, d) pairs without building list
|
|
448
|
+
i, j = np.divmod(ijs, d)
|
|
449
|
+
|
|
450
|
+
data = randn(nnz, dtype=dtype)
|
|
451
|
+
mat = sp.coo_matrix((data, (i, j)), shape=(d, d)).asformat(stype)
|
|
452
|
+
else:
|
|
453
|
+
density = 1.0
|
|
454
|
+
mat = qarray(randn((d, d), dtype=dtype))
|
|
455
|
+
|
|
456
|
+
if scaled:
|
|
457
|
+
mat /= ((2 if iscomplex else 1) * d * density) ** 0.5
|
|
458
|
+
|
|
459
|
+
return mat
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
@random_seed_fn
|
|
463
|
+
def rand_herm(d, sparse=False, density=None, dtype=complex):
|
|
464
|
+
"""Generate a random hermitian operator of order `d` with normally
|
|
465
|
+
distributed entries. In the limit of large `d` the spectrum will be a
|
|
466
|
+
semi-circular distribution between [-1, 1].
|
|
467
|
+
|
|
468
|
+
See Also
|
|
469
|
+
--------
|
|
470
|
+
rand_matrix, rand_pos, rand_rho, rand_uni
|
|
471
|
+
"""
|
|
472
|
+
if sparse:
|
|
473
|
+
density = 10 / d if density is None else density
|
|
474
|
+
density = min(max(density, d**-2), 1 - d**-2)
|
|
475
|
+
density /= 2 # to account of herm construction
|
|
476
|
+
|
|
477
|
+
herm = rand_matrix(
|
|
478
|
+
d, scaled=True, sparse=sparse, density=density, dtype=dtype
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
if sparse:
|
|
482
|
+
herm.data /= 2**1.5
|
|
483
|
+
else:
|
|
484
|
+
herm /= 2**1.5
|
|
485
|
+
|
|
486
|
+
herm += dag(herm)
|
|
487
|
+
|
|
488
|
+
return herm
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
@random_seed_fn
|
|
492
|
+
def rand_pos(d, sparse=False, density=None, dtype=complex):
|
|
493
|
+
"""Generate a random positive operator of size `d`, with normally
|
|
494
|
+
distributed entries. In the limit of large `d` the spectrum will lie
|
|
495
|
+
between [0, 1].
|
|
496
|
+
|
|
497
|
+
See Also
|
|
498
|
+
--------
|
|
499
|
+
rand_matrix, rand_herm, rand_rho, rand_uni
|
|
500
|
+
"""
|
|
501
|
+
if sparse:
|
|
502
|
+
density = 10 / d if density is None else density
|
|
503
|
+
density = min(max(density, d**-2), 1 - d**-2)
|
|
504
|
+
density = 0.5 * (density / d) ** 0.5 # to account for pos construction
|
|
505
|
+
|
|
506
|
+
pos = rand_matrix(
|
|
507
|
+
d, scaled=True, sparse=sparse, density=density, dtype=dtype
|
|
508
|
+
)
|
|
509
|
+
|
|
510
|
+
return dot(pos, dag(pos))
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
@random_seed_fn
|
|
514
|
+
def rand_rho(d, sparse=False, density=None, dtype=complex):
|
|
515
|
+
"""Generate a random positive operator of size `d` with normally
|
|
516
|
+
distributed entries and unit trace.
|
|
517
|
+
|
|
518
|
+
See Also
|
|
519
|
+
--------
|
|
520
|
+
rand_matrix, rand_herm, rand_pos, rand_uni
|
|
521
|
+
"""
|
|
522
|
+
return nmlz(rand_pos(d, sparse=sparse, density=density, dtype=dtype))
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
@random_seed_fn
|
|
526
|
+
def rand_uni(d, dtype=complex):
|
|
527
|
+
"""Generate a random unitary operator of size `d`, distributed according to
|
|
528
|
+
the Haar measure.
|
|
529
|
+
|
|
530
|
+
See Also
|
|
531
|
+
--------
|
|
532
|
+
rand_matrix, rand_herm, rand_pos, rand_rho
|
|
533
|
+
"""
|
|
534
|
+
q, r = np.linalg.qr(rand_matrix(d, dtype=dtype))
|
|
535
|
+
r = np.diagonal(r)
|
|
536
|
+
r = r / np.abs(r) # read-only so not inplace
|
|
537
|
+
return rdmul(q, r)
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
@random_seed_fn
|
|
541
|
+
def rand_ket(d, sparse=False, stype="csr", density=0.01, dtype=complex):
|
|
542
|
+
"""Generates a ket of length `d` with normally distributed entries."""
|
|
543
|
+
if sparse:
|
|
544
|
+
ket = sp.random(d, 1, format=stype, density=density)
|
|
545
|
+
ket.data = randn((ket.nnz,), dtype=dtype)
|
|
546
|
+
else:
|
|
547
|
+
ket = qarray(randn((d, 1), dtype=dtype))
|
|
548
|
+
return nmlz(ket)
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
@random_seed_fn
|
|
552
|
+
def rand_haar_state(d, dtype=complex):
|
|
553
|
+
"""Generate a random state of dimension `d` according to the Haar
|
|
554
|
+
distribution.
|
|
555
|
+
"""
|
|
556
|
+
u = rand_uni(d, dtype=dtype)
|
|
557
|
+
return u[:, [0]]
|
|
558
|
+
|
|
559
|
+
|
|
560
|
+
@random_seed_fn
|
|
561
|
+
def gen_rand_haar_states(d, reps, dtype=complex):
|
|
562
|
+
"""Generate many random Haar states, recycling a random unitary operator
|
|
563
|
+
by using all of its columns (not a good idea?).
|
|
564
|
+
"""
|
|
565
|
+
for rep in range(reps):
|
|
566
|
+
cyc = rep % d
|
|
567
|
+
if cyc == 0:
|
|
568
|
+
u = rand_uni(d, dtype=dtype)
|
|
569
|
+
yield u[:, [cyc]]
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
@random_seed_fn
|
|
573
|
+
def rand_mix(d, tr_d_min=None, tr_d_max=None, mode="rand", dtype=complex):
|
|
574
|
+
"""Constructs a random mixed state by tracing out a random ket
|
|
575
|
+
where the composite system varies in size between 2 and d. This produces
|
|
576
|
+
a spread of states including more purity but has no real meaning.
|
|
577
|
+
"""
|
|
578
|
+
if tr_d_min is None:
|
|
579
|
+
tr_d_min = 2
|
|
580
|
+
if tr_d_max is None:
|
|
581
|
+
tr_d_max = d
|
|
582
|
+
|
|
583
|
+
m = _randint(tr_d_min, tr_d_max)
|
|
584
|
+
if mode == "rand":
|
|
585
|
+
psi = rand_ket(d * m, dtype=dtype)
|
|
586
|
+
elif mode == "haar":
|
|
587
|
+
psi = rand_haar_state(d * m, dtype=dtype)
|
|
588
|
+
|
|
589
|
+
return ptr(psi, [d, m], 0)
|
|
590
|
+
|
|
591
|
+
|
|
592
|
+
@random_seed_fn
|
|
593
|
+
def rand_product_state(n, qtype=None, dtype=complex):
|
|
594
|
+
"""Generates a ket of `n` many random pure qubits."""
|
|
595
|
+
|
|
596
|
+
def gen_rand_pure_qubits(n):
|
|
597
|
+
for _ in range(n):
|
|
598
|
+
(u,) = rand(1)
|
|
599
|
+
(v,) = rand(1)
|
|
600
|
+
phi = 2 * np.pi * u
|
|
601
|
+
theta = np.arccos(2 * v - 1)
|
|
602
|
+
yield qu(
|
|
603
|
+
[
|
|
604
|
+
[np.cos(theta / 2.0)],
|
|
605
|
+
[np.sin(theta / 2.0) * np.exp(1.0j * phi)],
|
|
606
|
+
],
|
|
607
|
+
qtype=qtype,
|
|
608
|
+
dtype=dtype,
|
|
609
|
+
)
|
|
610
|
+
|
|
611
|
+
return kron(*gen_rand_pure_qubits(n))
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
@random_seed_fn
|
|
615
|
+
def rand_matrix_product_state(
|
|
616
|
+
n, bond_dim, phys_dim=2, dtype=complex, cyclic=False, trans_invar=False
|
|
617
|
+
):
|
|
618
|
+
"""Generate a random matrix product state (in dense form, see
|
|
619
|
+
:func:`~quimb.tensor.MPS_rand_state` for tensor network form).
|
|
620
|
+
|
|
621
|
+
Parameters
|
|
622
|
+
----------
|
|
623
|
+
n : int
|
|
624
|
+
Number of sites.
|
|
625
|
+
bond_dim : int
|
|
626
|
+
Dimension of the bond (virtual) indices.
|
|
627
|
+
phys_dim : int, optional
|
|
628
|
+
Physical dimension of each local site, defaults to 2 (qubits).
|
|
629
|
+
cyclic : bool (optional)
|
|
630
|
+
Whether to impose cyclic boundary conditions on the entanglement
|
|
631
|
+
structure.
|
|
632
|
+
trans_invar : bool (optional)
|
|
633
|
+
Whether to generate a translationally invariant state,
|
|
634
|
+
requires cyclic=True.
|
|
635
|
+
|
|
636
|
+
Returns
|
|
637
|
+
-------
|
|
638
|
+
ket : qarray
|
|
639
|
+
The random state, with shape (phys_dim**n, 1)
|
|
640
|
+
|
|
641
|
+
"""
|
|
642
|
+
from quimb.tensor import MPS_rand_state
|
|
643
|
+
|
|
644
|
+
mps = MPS_rand_state(
|
|
645
|
+
n,
|
|
646
|
+
bond_dim,
|
|
647
|
+
phys_dim=phys_dim,
|
|
648
|
+
dtype=dtype,
|
|
649
|
+
cyclic=cyclic,
|
|
650
|
+
trans_invar=trans_invar,
|
|
651
|
+
)
|
|
652
|
+
return mps.to_dense()
|
|
653
|
+
|
|
654
|
+
|
|
655
|
+
rand_mps = rand_matrix_product_state
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
@random_seed_fn
|
|
659
|
+
def rand_seperable(dims, num_mix=10, dtype=complex):
|
|
660
|
+
"""Generate a random, mixed, seperable state. E.g rand_seperable([2, 2])
|
|
661
|
+
for a mixed two qubit state with no entanglement.
|
|
662
|
+
|
|
663
|
+
Parameters
|
|
664
|
+
----------
|
|
665
|
+
dims : tuple of int
|
|
666
|
+
The local dimensions across which to be seperable.
|
|
667
|
+
num_mix : int, optional
|
|
668
|
+
How many individual product states to sum together, each with
|
|
669
|
+
random weight.
|
|
670
|
+
|
|
671
|
+
Returns
|
|
672
|
+
-------
|
|
673
|
+
qarray
|
|
674
|
+
Mixed seperable state.
|
|
675
|
+
"""
|
|
676
|
+
|
|
677
|
+
def gen_single_sites():
|
|
678
|
+
for dim in dims:
|
|
679
|
+
yield rand_rho(dim, dtype=dtype)
|
|
680
|
+
|
|
681
|
+
weights = rand(num_mix)
|
|
682
|
+
|
|
683
|
+
def gen_single_states():
|
|
684
|
+
for w in weights:
|
|
685
|
+
yield w * kron(*gen_single_sites())
|
|
686
|
+
|
|
687
|
+
return sum(gen_single_states()) / np.sum(weights)
|
|
688
|
+
|
|
689
|
+
|
|
690
|
+
@random_seed_fn
|
|
691
|
+
def rand_iso(n, m, dtype=complex):
|
|
692
|
+
"""Generate a random isometry of shape ``(n, m)``."""
|
|
693
|
+
data = randn((n, m), dtype=dtype)
|
|
694
|
+
|
|
695
|
+
q, _ = np.linalg.qr(data if n >= m else data.T)
|
|
696
|
+
q = q.astype(dtype)
|
|
697
|
+
|
|
698
|
+
return q if (n >= m) else q.T
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
@random_seed_fn
|
|
702
|
+
def rand_mera(n, invariant=False, dtype=complex):
|
|
703
|
+
"""Generate a random mera state of ``n`` qubits, which must be a power
|
|
704
|
+
of 2. This uses ``quimb.tensor``.
|
|
705
|
+
"""
|
|
706
|
+
import quimb.tensor as qt
|
|
707
|
+
|
|
708
|
+
if invariant:
|
|
709
|
+
constructor = qt.MERA.rand_invar
|
|
710
|
+
else:
|
|
711
|
+
constructor = qt.MERA.rand
|
|
712
|
+
|
|
713
|
+
return constructor(n, dtype=dtype).to_qarray()
|