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.
Files changed (122) hide show
  1. trajectree/__init__.py +3 -0
  2. trajectree/fock_optics/devices.py +1 -1
  3. trajectree/fock_optics/light_sources.py +2 -2
  4. trajectree/fock_optics/measurement.py +3 -3
  5. trajectree/fock_optics/utils.py +6 -6
  6. trajectree/quimb/docs/_pygments/_pygments_dark.py +118 -0
  7. trajectree/quimb/docs/_pygments/_pygments_light.py +118 -0
  8. trajectree/quimb/docs/conf.py +158 -0
  9. trajectree/quimb/docs/examples/ex_mpi_expm_evo.py +62 -0
  10. trajectree/quimb/quimb/__init__.py +507 -0
  11. trajectree/quimb/quimb/calc.py +1491 -0
  12. trajectree/quimb/quimb/core.py +2279 -0
  13. trajectree/quimb/quimb/evo.py +712 -0
  14. trajectree/quimb/quimb/experimental/__init__.py +0 -0
  15. trajectree/quimb/quimb/experimental/autojittn.py +129 -0
  16. trajectree/quimb/quimb/experimental/belief_propagation/__init__.py +109 -0
  17. trajectree/quimb/quimb/experimental/belief_propagation/bp_common.py +397 -0
  18. trajectree/quimb/quimb/experimental/belief_propagation/d1bp.py +316 -0
  19. trajectree/quimb/quimb/experimental/belief_propagation/d2bp.py +653 -0
  20. trajectree/quimb/quimb/experimental/belief_propagation/hd1bp.py +571 -0
  21. trajectree/quimb/quimb/experimental/belief_propagation/hv1bp.py +775 -0
  22. trajectree/quimb/quimb/experimental/belief_propagation/l1bp.py +316 -0
  23. trajectree/quimb/quimb/experimental/belief_propagation/l2bp.py +537 -0
  24. trajectree/quimb/quimb/experimental/belief_propagation/regions.py +194 -0
  25. trajectree/quimb/quimb/experimental/cluster_update.py +286 -0
  26. trajectree/quimb/quimb/experimental/merabuilder.py +865 -0
  27. trajectree/quimb/quimb/experimental/operatorbuilder/__init__.py +15 -0
  28. trajectree/quimb/quimb/experimental/operatorbuilder/operatorbuilder.py +1631 -0
  29. trajectree/quimb/quimb/experimental/schematic.py +7 -0
  30. trajectree/quimb/quimb/experimental/tn_marginals.py +130 -0
  31. trajectree/quimb/quimb/experimental/tnvmc.py +1483 -0
  32. trajectree/quimb/quimb/gates.py +36 -0
  33. trajectree/quimb/quimb/gen/__init__.py +2 -0
  34. trajectree/quimb/quimb/gen/operators.py +1167 -0
  35. trajectree/quimb/quimb/gen/rand.py +713 -0
  36. trajectree/quimb/quimb/gen/states.py +479 -0
  37. trajectree/quimb/quimb/linalg/__init__.py +6 -0
  38. trajectree/quimb/quimb/linalg/approx_spectral.py +1109 -0
  39. trajectree/quimb/quimb/linalg/autoblock.py +258 -0
  40. trajectree/quimb/quimb/linalg/base_linalg.py +719 -0
  41. trajectree/quimb/quimb/linalg/mpi_launcher.py +397 -0
  42. trajectree/quimb/quimb/linalg/numpy_linalg.py +244 -0
  43. trajectree/quimb/quimb/linalg/rand_linalg.py +514 -0
  44. trajectree/quimb/quimb/linalg/scipy_linalg.py +293 -0
  45. trajectree/quimb/quimb/linalg/slepc_linalg.py +892 -0
  46. trajectree/quimb/quimb/schematic.py +1518 -0
  47. trajectree/quimb/quimb/tensor/__init__.py +401 -0
  48. trajectree/quimb/quimb/tensor/array_ops.py +610 -0
  49. trajectree/quimb/quimb/tensor/circuit.py +4824 -0
  50. trajectree/quimb/quimb/tensor/circuit_gen.py +411 -0
  51. trajectree/quimb/quimb/tensor/contraction.py +336 -0
  52. trajectree/quimb/quimb/tensor/decomp.py +1255 -0
  53. trajectree/quimb/quimb/tensor/drawing.py +1646 -0
  54. trajectree/quimb/quimb/tensor/fitting.py +385 -0
  55. trajectree/quimb/quimb/tensor/geometry.py +583 -0
  56. trajectree/quimb/quimb/tensor/interface.py +114 -0
  57. trajectree/quimb/quimb/tensor/networking.py +1058 -0
  58. trajectree/quimb/quimb/tensor/optimize.py +1818 -0
  59. trajectree/quimb/quimb/tensor/tensor_1d.py +4778 -0
  60. trajectree/quimb/quimb/tensor/tensor_1d_compress.py +1854 -0
  61. trajectree/quimb/quimb/tensor/tensor_1d_tebd.py +662 -0
  62. trajectree/quimb/quimb/tensor/tensor_2d.py +5954 -0
  63. trajectree/quimb/quimb/tensor/tensor_2d_compress.py +96 -0
  64. trajectree/quimb/quimb/tensor/tensor_2d_tebd.py +1230 -0
  65. trajectree/quimb/quimb/tensor/tensor_3d.py +2869 -0
  66. trajectree/quimb/quimb/tensor/tensor_3d_tebd.py +46 -0
  67. trajectree/quimb/quimb/tensor/tensor_approx_spectral.py +60 -0
  68. trajectree/quimb/quimb/tensor/tensor_arbgeom.py +3237 -0
  69. trajectree/quimb/quimb/tensor/tensor_arbgeom_compress.py +565 -0
  70. trajectree/quimb/quimb/tensor/tensor_arbgeom_tebd.py +1138 -0
  71. trajectree/quimb/quimb/tensor/tensor_builder.py +5411 -0
  72. trajectree/quimb/quimb/tensor/tensor_core.py +11179 -0
  73. trajectree/quimb/quimb/tensor/tensor_dmrg.py +1472 -0
  74. trajectree/quimb/quimb/tensor/tensor_mera.py +204 -0
  75. trajectree/quimb/quimb/utils.py +892 -0
  76. trajectree/quimb/tests/__init__.py +0 -0
  77. trajectree/quimb/tests/test_accel.py +501 -0
  78. trajectree/quimb/tests/test_calc.py +788 -0
  79. trajectree/quimb/tests/test_core.py +847 -0
  80. trajectree/quimb/tests/test_evo.py +565 -0
  81. trajectree/quimb/tests/test_gen/__init__.py +0 -0
  82. trajectree/quimb/tests/test_gen/test_operators.py +361 -0
  83. trajectree/quimb/tests/test_gen/test_rand.py +296 -0
  84. trajectree/quimb/tests/test_gen/test_states.py +261 -0
  85. trajectree/quimb/tests/test_linalg/__init__.py +0 -0
  86. trajectree/quimb/tests/test_linalg/test_approx_spectral.py +368 -0
  87. trajectree/quimb/tests/test_linalg/test_base_linalg.py +351 -0
  88. trajectree/quimb/tests/test_linalg/test_mpi_linalg.py +127 -0
  89. trajectree/quimb/tests/test_linalg/test_numpy_linalg.py +84 -0
  90. trajectree/quimb/tests/test_linalg/test_rand_linalg.py +134 -0
  91. trajectree/quimb/tests/test_linalg/test_slepc_linalg.py +283 -0
  92. trajectree/quimb/tests/test_tensor/__init__.py +0 -0
  93. trajectree/quimb/tests/test_tensor/test_belief_propagation/__init__.py +0 -0
  94. trajectree/quimb/tests/test_tensor/test_belief_propagation/test_d1bp.py +39 -0
  95. trajectree/quimb/tests/test_tensor/test_belief_propagation/test_d2bp.py +67 -0
  96. trajectree/quimb/tests/test_tensor/test_belief_propagation/test_hd1bp.py +64 -0
  97. trajectree/quimb/tests/test_tensor/test_belief_propagation/test_hv1bp.py +51 -0
  98. trajectree/quimb/tests/test_tensor/test_belief_propagation/test_l1bp.py +142 -0
  99. trajectree/quimb/tests/test_tensor/test_belief_propagation/test_l2bp.py +101 -0
  100. trajectree/quimb/tests/test_tensor/test_circuit.py +816 -0
  101. trajectree/quimb/tests/test_tensor/test_contract.py +67 -0
  102. trajectree/quimb/tests/test_tensor/test_decomp.py +40 -0
  103. trajectree/quimb/tests/test_tensor/test_mera.py +52 -0
  104. trajectree/quimb/tests/test_tensor/test_optimizers.py +488 -0
  105. trajectree/quimb/tests/test_tensor/test_tensor_1d.py +1171 -0
  106. trajectree/quimb/tests/test_tensor/test_tensor_2d.py +606 -0
  107. trajectree/quimb/tests/test_tensor/test_tensor_2d_tebd.py +144 -0
  108. trajectree/quimb/tests/test_tensor/test_tensor_3d.py +123 -0
  109. trajectree/quimb/tests/test_tensor/test_tensor_arbgeom.py +226 -0
  110. trajectree/quimb/tests/test_tensor/test_tensor_builder.py +441 -0
  111. trajectree/quimb/tests/test_tensor/test_tensor_core.py +2066 -0
  112. trajectree/quimb/tests/test_tensor/test_tensor_dmrg.py +388 -0
  113. trajectree/quimb/tests/test_tensor/test_tensor_spectral_approx.py +63 -0
  114. trajectree/quimb/tests/test_tensor/test_tensor_tebd.py +270 -0
  115. trajectree/quimb/tests/test_utils.py +85 -0
  116. trajectree/trajectory.py +2 -2
  117. {trajectree-0.0.0.dist-info → trajectree-0.0.1.dist-info}/METADATA +2 -2
  118. trajectree-0.0.1.dist-info/RECORD +126 -0
  119. trajectree-0.0.0.dist-info/RECORD +0 -16
  120. {trajectree-0.0.0.dist-info → trajectree-0.0.1.dist-info}/WHEEL +0 -0
  121. {trajectree-0.0.0.dist-info → trajectree-0.0.1.dist-info}/licenses/LICENSE +0 -0
  122. {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()