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,847 @@
|
|
|
1
|
+
import itertools
|
|
2
|
+
|
|
3
|
+
from pytest import fixture, raises, mark
|
|
4
|
+
import scipy.sparse as sp
|
|
5
|
+
import numpy as np
|
|
6
|
+
from numpy.testing import assert_allclose, assert_almost_equal
|
|
7
|
+
|
|
8
|
+
import quimb as qu
|
|
9
|
+
|
|
10
|
+
stypes = ("csr", "csc", "bsr", "coo")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@fixture
|
|
14
|
+
def od1():
|
|
15
|
+
return qu.rand_matrix(3)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@fixture
|
|
19
|
+
def os1():
|
|
20
|
+
return qu.rand_matrix(3, sparse=True, density=0.5)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class TestSparseMatrix:
|
|
24
|
+
@mark.parametrize("stype", stypes)
|
|
25
|
+
def test_simple(self, stype):
|
|
26
|
+
a = qu.core.sparse_matrix([[0, 3], [1, 2]], stype)
|
|
27
|
+
assert a.format == stype
|
|
28
|
+
assert a.dtype == complex
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class TestQuimbify:
|
|
32
|
+
def test_vector_create(self):
|
|
33
|
+
x = [1, 2, 3j]
|
|
34
|
+
p = qu.qu(x, qtype="ket")
|
|
35
|
+
assert type(p) == qu.qarray
|
|
36
|
+
assert p.dtype == complex
|
|
37
|
+
assert p.shape == (3, 1)
|
|
38
|
+
p = qu.qu(x, qtype="bra")
|
|
39
|
+
assert p.shape == (1, 3)
|
|
40
|
+
assert_almost_equal(p[0, 2], -3.0j)
|
|
41
|
+
|
|
42
|
+
def test_dop_create(self):
|
|
43
|
+
x = np.random.randn(3, 3)
|
|
44
|
+
p = qu.qu(x, qtype="dop")
|
|
45
|
+
assert type(p) == qu.qarray
|
|
46
|
+
assert p.dtype == complex
|
|
47
|
+
assert p.shape == (3, 3)
|
|
48
|
+
|
|
49
|
+
def test_convert_vector_to_dop(self):
|
|
50
|
+
x = [1, 2, 3j]
|
|
51
|
+
p = qu.qu(x, qtype="r")
|
|
52
|
+
assert_allclose(
|
|
53
|
+
p,
|
|
54
|
+
qu.qarray(
|
|
55
|
+
[
|
|
56
|
+
[1.0 + 0.0j, 2.0 + 0.0j, 0.0 - 3.0j],
|
|
57
|
+
[2.0 + 0.0j, 4.0 + 0.0j, 0.0 - 6.0j],
|
|
58
|
+
[0.0 + 3.0j, 0.0 + 6.0j, 9.0 + 0.0j],
|
|
59
|
+
]
|
|
60
|
+
),
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
def test_chopped(self):
|
|
64
|
+
x = [9e-16, 1]
|
|
65
|
+
p = qu.qu(x, "k", chopped=False)
|
|
66
|
+
assert p[0, 0] != 0.0
|
|
67
|
+
p = qu.qu(x, "k", chopped=True)
|
|
68
|
+
assert p[0, 0] == 0.0
|
|
69
|
+
|
|
70
|
+
def test_normalized(self):
|
|
71
|
+
x = [3j, 4j]
|
|
72
|
+
p = qu.qu(x, "k", normalized=False)
|
|
73
|
+
assert_almost_equal(qu.tr(p.H @ p), 25.0)
|
|
74
|
+
p = qu.qu(x, "k", normalized=True)
|
|
75
|
+
assert_almost_equal(qu.tr(p.H @ p), 1.0)
|
|
76
|
+
p = qu.qu(x, "dop", normalized=True)
|
|
77
|
+
assert_almost_equal(qu.tr(p), 1.0)
|
|
78
|
+
|
|
79
|
+
def test_sparse_create(self):
|
|
80
|
+
x = [[1, 0], [3, 0]]
|
|
81
|
+
p = qu.qu(x, "dop", sparse=False)
|
|
82
|
+
assert type(p) == qu.qarray
|
|
83
|
+
p = qu.qu(x, "dop", sparse=True)
|
|
84
|
+
assert type(p) == sp.csr_matrix
|
|
85
|
+
assert p.dtype == complex
|
|
86
|
+
assert p.nnz == 2
|
|
87
|
+
|
|
88
|
+
def test_sparse_convert_to_dop(self):
|
|
89
|
+
x = [1, 0, 9e-16, 0, 3j]
|
|
90
|
+
p = qu.qu(x, "ket", sparse=True)
|
|
91
|
+
q = qu.qu(p, "dop", sparse=True)
|
|
92
|
+
assert q.shape == (5, 5)
|
|
93
|
+
assert q.nnz == 9
|
|
94
|
+
assert_almost_equal(q[4, 4], 9.0)
|
|
95
|
+
q = qu.qu(p, "dop", sparse=True, normalized=True)
|
|
96
|
+
assert_almost_equal(qu.tr(q), 1.0)
|
|
97
|
+
|
|
98
|
+
@mark.parametrize(
|
|
99
|
+
"qtype, shape, out",
|
|
100
|
+
(
|
|
101
|
+
("bra", (1, 4), [[1, 0, 2, -3j]]),
|
|
102
|
+
("ket", (4, 1), [[1], [0], [2], [3j]]),
|
|
103
|
+
(
|
|
104
|
+
"dop",
|
|
105
|
+
(4, 4),
|
|
106
|
+
[[1, 0, 2, -3j], [0, 0, 0, 0], [2, 0, 4, -6j], [3j, 0, 6j, 9]],
|
|
107
|
+
),
|
|
108
|
+
),
|
|
109
|
+
)
|
|
110
|
+
@mark.parametrize("format_in", stypes)
|
|
111
|
+
@mark.parametrize("format_out", (None,) + stypes)
|
|
112
|
+
@mark.parametrize("dtype", [float, complex, np.float64, np.complex128])
|
|
113
|
+
def test_reshape_sparse(
|
|
114
|
+
self, qtype, shape, out, format_in, format_out, dtype
|
|
115
|
+
):
|
|
116
|
+
import warnings
|
|
117
|
+
|
|
118
|
+
in_ = [[1], [0], [2], [3j]]
|
|
119
|
+
x = qu.core.sparse_matrix(in_, stype=format_in)
|
|
120
|
+
|
|
121
|
+
with warnings.catch_warnings():
|
|
122
|
+
warnings.simplefilter("ignore")
|
|
123
|
+
y = qu.qu(x, qtype=qtype, stype=format_out, dtype=dtype)
|
|
124
|
+
|
|
125
|
+
assert y.shape == shape
|
|
126
|
+
assert y.dtype == dtype
|
|
127
|
+
if format_out is None:
|
|
128
|
+
format_out = format_in
|
|
129
|
+
assert y.format == format_out
|
|
130
|
+
|
|
131
|
+
if np.issubdtype(dtype, np.floating):
|
|
132
|
+
assert_allclose(y.toarray(), np.real(out), atol=1e-12)
|
|
133
|
+
else:
|
|
134
|
+
assert_allclose(y.toarray(), out)
|
|
135
|
+
|
|
136
|
+
@mark.parametrize(
|
|
137
|
+
"qtype, shape, out",
|
|
138
|
+
(
|
|
139
|
+
("bra", (1, 4), [[1, 0, 2, -3j]]),
|
|
140
|
+
("ket", (4, 1), [[1], [0], [2], [3j]]),
|
|
141
|
+
(
|
|
142
|
+
"dop",
|
|
143
|
+
(4, 4),
|
|
144
|
+
[[1, 0, 2, -3j], [0, 0, 0, 0], [2, 0, 4, -6j], [3j, 0, 6j, 9]],
|
|
145
|
+
),
|
|
146
|
+
),
|
|
147
|
+
)
|
|
148
|
+
@mark.parametrize("format_out", (None,) + stypes)
|
|
149
|
+
def test_dense_to_sparse_format(self, qtype, shape, out, format_out):
|
|
150
|
+
x = [[1], [0], [2], [3j]]
|
|
151
|
+
y = qu.qu(x, qtype=qtype, stype=format_out, sparse=True)
|
|
152
|
+
assert y.shape == shape
|
|
153
|
+
assert y.dtype == complex
|
|
154
|
+
if format_out is None:
|
|
155
|
+
format_out = "csr"
|
|
156
|
+
assert y.format == format_out
|
|
157
|
+
assert_allclose(y.toarray(), out)
|
|
158
|
+
|
|
159
|
+
@mark.parametrize(
|
|
160
|
+
"qtype, shape", (["bra", (1, 4)], ["ket", (4, 1)], ["dop", (4, 4)])
|
|
161
|
+
)
|
|
162
|
+
@mark.parametrize("format_out", stypes)
|
|
163
|
+
def test_give_sformat_only(self, qtype, shape, format_out):
|
|
164
|
+
x = [[1], [0], [2], [3j]]
|
|
165
|
+
y = qu.qu(x, qtype=qtype, stype=format_out)
|
|
166
|
+
assert qu.issparse(y)
|
|
167
|
+
assert y.shape == shape
|
|
168
|
+
assert y.format == format_out
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class TestInferSize:
|
|
172
|
+
@mark.parametrize(
|
|
173
|
+
"d,base,n", ([8, 2, 3], [16, 2, 4], [9, 3, 2], [81, 3, 4])
|
|
174
|
+
)
|
|
175
|
+
def test_infer_size(self, d, base, n):
|
|
176
|
+
p = qu.rand_ket(d)
|
|
177
|
+
assert qu.infer_size(p, base) == n
|
|
178
|
+
|
|
179
|
+
def test_raises(self):
|
|
180
|
+
p = qu.rand_ket(2) & qu.rand_ket(3)
|
|
181
|
+
with raises(ValueError):
|
|
182
|
+
qu.infer_size(p, base=2)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
class TestTrace:
|
|
186
|
+
@mark.parametrize(
|
|
187
|
+
"inpt, outpt", ([[[2, 1], [4, 5]], 7], [[[2, 1], [4, 5j]], 2 + 5j])
|
|
188
|
+
)
|
|
189
|
+
@mark.parametrize(
|
|
190
|
+
"sparse, func",
|
|
191
|
+
([False, qu.core._trace_dense], [True, qu.core._trace_sparse]),
|
|
192
|
+
)
|
|
193
|
+
def test_simple(self, inpt, outpt, sparse, func):
|
|
194
|
+
a = qu.qu(inpt, sparse=sparse)
|
|
195
|
+
assert qu.trace(a) == outpt
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class TestITrace:
|
|
199
|
+
@mark.parametrize("axes", [(0, 1), ((0,), (1,))])
|
|
200
|
+
def test_axes_types(self, axes):
|
|
201
|
+
a = qu.rand_matrix(4)
|
|
202
|
+
b = qu.itrace(a, axes)
|
|
203
|
+
assert_allclose(b, np.trace(a))
|
|
204
|
+
|
|
205
|
+
def test_complex_dims(self):
|
|
206
|
+
a = np.random.rand(4, 3, 2, 2, 4, 3)
|
|
207
|
+
atr = qu.itrace(a, ((0, 1, 2), (4, 5, 3)))
|
|
208
|
+
btr = np.trace(
|
|
209
|
+
np.trace(np.trace(a, axis1=1, axis2=5), axis1=1, axis2=2)
|
|
210
|
+
)
|
|
211
|
+
assert_allclose(atr, btr)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
class TestNormalize:
|
|
215
|
+
def test_normalize_ket(self):
|
|
216
|
+
a = qu.qu([1, -1j], "ket")
|
|
217
|
+
b = qu.nmlz(a, inplace=False)
|
|
218
|
+
assert_almost_equal(qu.trace(b.H @ b), 1.0)
|
|
219
|
+
assert_almost_equal(qu.trace(a.H @ a), 2.0)
|
|
220
|
+
|
|
221
|
+
def test_normalize_bra(self):
|
|
222
|
+
a = qu.qu([1, -1j], "bra")
|
|
223
|
+
b = qu.nmlz(a, inplace=False)
|
|
224
|
+
assert_almost_equal(qu.trace(b @ b.H), 1.0)
|
|
225
|
+
|
|
226
|
+
def test_normalize_dop(self):
|
|
227
|
+
a = qu.qu([1, -1j], "dop")
|
|
228
|
+
b = qu.nmlz(a, inplace=False)
|
|
229
|
+
assert_almost_equal(qu.trace(b), 1.0)
|
|
230
|
+
|
|
231
|
+
def test_normalize_inplace_ket(self):
|
|
232
|
+
a = qu.qu([1, -1j], "ket")
|
|
233
|
+
a.nmlz(inplace=True)
|
|
234
|
+
assert_almost_equal(qu.trace(a.H @ a), 1.0)
|
|
235
|
+
|
|
236
|
+
def test_normalize_inplace_bra(self):
|
|
237
|
+
a = qu.qu([1, -1j], "bra")
|
|
238
|
+
a.nmlz(inplace=True)
|
|
239
|
+
assert_almost_equal(qu.trace(a @ a.H), 1.0)
|
|
240
|
+
|
|
241
|
+
def test_normalize_inplace_dop(self):
|
|
242
|
+
a = qu.qu([1, -1j], "dop")
|
|
243
|
+
b = qu.nmlz(a, inplace=True)
|
|
244
|
+
assert_almost_equal(qu.trace(a), 1.0)
|
|
245
|
+
assert_almost_equal(qu.trace(b), 1.0)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
class TestDimMap:
|
|
249
|
+
@mark.parametrize("numpy", [False, True])
|
|
250
|
+
def test_1d(self, numpy):
|
|
251
|
+
dims = [10, 11, 12, 13]
|
|
252
|
+
coos = (1, 2, 3)
|
|
253
|
+
if numpy:
|
|
254
|
+
dims, coos = np.asarray(dims), np.asarray(coos)
|
|
255
|
+
ndims, ncoos = qu.dim_map(dims, coos)
|
|
256
|
+
assert_allclose([ndims[c] for c in ncoos], (11, 12, 13))
|
|
257
|
+
coos = ([-1], [2], [5])
|
|
258
|
+
with raises(ValueError):
|
|
259
|
+
ndims, ncoos = qu.dim_map(dims, coos)
|
|
260
|
+
ndims, ncoos = qu.dim_map(dims, coos, cyclic=True)
|
|
261
|
+
assert_allclose([ndims[c] for c in ncoos], (13, 12, 11))
|
|
262
|
+
ndims, ncoos = qu.dim_map(dims, coos, trim=True)
|
|
263
|
+
assert_allclose([ndims[c] for c in ncoos], [12])
|
|
264
|
+
|
|
265
|
+
def test_2d(self):
|
|
266
|
+
dims = [[200, 201, 202, 203], [210, 211, 212, 213]]
|
|
267
|
+
coos = ((1, 2), (1, 3), (0, 3))
|
|
268
|
+
ndims, ncoos = qu.dim_map(dims, coos)
|
|
269
|
+
assert_allclose([ndims[c] for c in ncoos], (212, 213, 203))
|
|
270
|
+
coos = ((-1, 1), (1, 2), (3, 4))
|
|
271
|
+
with raises(ValueError):
|
|
272
|
+
ndims, ncoos = qu.dim_map(dims, coos)
|
|
273
|
+
ndims, ncoos = qu.dim_map(dims, coos, cyclic=True)
|
|
274
|
+
assert_allclose([ndims[c] for c in ncoos], (211, 212, 210))
|
|
275
|
+
ndims, ncoos = qu.dim_map(dims, coos, trim=True)
|
|
276
|
+
assert_allclose([ndims[c] for c in ncoos], [212])
|
|
277
|
+
|
|
278
|
+
def test_3d(self):
|
|
279
|
+
dims = [
|
|
280
|
+
[[3000, 3001, 3002], [3010, 3011, 3012], [3020, 3021, 3022]],
|
|
281
|
+
[[3100, 3101, 3102], [3110, 3111, 3112], [3120, 3121, 3122]],
|
|
282
|
+
]
|
|
283
|
+
coos = ((0, 0, 2), (1, 1, 2), (1, 2, 0))
|
|
284
|
+
ndims, ncoos = qu.dim_map(dims, coos)
|
|
285
|
+
assert_allclose([ndims[c] for c in ncoos], (3002, 3112, 3120))
|
|
286
|
+
coos = ((0, -1, 2), (1, 2, 2), (4, -1, 3))
|
|
287
|
+
with raises(ValueError):
|
|
288
|
+
ndims, ncoos = qu.dim_map(dims, coos)
|
|
289
|
+
ndims, ncoos = qu.dim_map(dims, coos, cyclic=True)
|
|
290
|
+
assert_allclose([ndims[c] for c in ncoos], (3022, 3122, 3020))
|
|
291
|
+
ndims, ncoos = qu.dim_map(dims, coos, trim=True)
|
|
292
|
+
assert_allclose([ndims[c] for c in ncoos], [3122])
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
class TestDimCompress:
|
|
296
|
+
def test_edge(self):
|
|
297
|
+
dims = [2, 3, 2, 4, 5]
|
|
298
|
+
coos = [0, 4]
|
|
299
|
+
ndims, ncoos = qu.dim_compress(dims, coos)
|
|
300
|
+
assert ndims == (2, 24, 5)
|
|
301
|
+
assert ncoos == (0, 2)
|
|
302
|
+
|
|
303
|
+
def test_middle(self):
|
|
304
|
+
dims = [5, 3, 2, 5, 4, 3, 2]
|
|
305
|
+
coos = [1, 2, 3, 5]
|
|
306
|
+
ndims, ncoos = qu.dim_compress(dims, coos)
|
|
307
|
+
assert ndims == (5, 30, 4, 3, 2)
|
|
308
|
+
assert ncoos == (1, 3)
|
|
309
|
+
|
|
310
|
+
def test_single(self):
|
|
311
|
+
dims = [5, 3, 2, 5, 4, 3, 2]
|
|
312
|
+
coos = 3
|
|
313
|
+
ndims, ncoos = qu.dim_compress(dims, coos)
|
|
314
|
+
assert ndims == (30, 5, 24)
|
|
315
|
+
assert ncoos == (1,)
|
|
316
|
+
|
|
317
|
+
@mark.parametrize(
|
|
318
|
+
"dims, inds, ndims, ninds",
|
|
319
|
+
[([2, 2], [0, 1], (4,), (0,)), ([4], [0], (4,), (0,))],
|
|
320
|
+
)
|
|
321
|
+
def test_tiny(self, dims, inds, ndims, ninds):
|
|
322
|
+
dims, inds = qu.dim_compress(dims, inds)
|
|
323
|
+
assert dims == ndims
|
|
324
|
+
assert inds == ninds
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
class TestEye:
|
|
328
|
+
def test_eye_dense(self):
|
|
329
|
+
a = qu.eye(3, sparse=False)
|
|
330
|
+
assert a.shape == (3, 3)
|
|
331
|
+
assert isinstance(a, qu.qarray)
|
|
332
|
+
assert a.dtype == complex
|
|
333
|
+
|
|
334
|
+
def test_eye_sparse(self):
|
|
335
|
+
a = qu.eye(3, sparse=True)
|
|
336
|
+
assert a.shape == (3, 3)
|
|
337
|
+
assert isinstance(a, sp.csr_matrix)
|
|
338
|
+
assert a.dtype == complex
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
class TestKron:
|
|
342
|
+
@mark.parametrize("parallel", [True, False])
|
|
343
|
+
def test_kron_basic(self, parallel):
|
|
344
|
+
a = qu.rand_ket(2)
|
|
345
|
+
b = qu.rand_ket(4)
|
|
346
|
+
c = qu.rand_ket(4)
|
|
347
|
+
d = qu.rand_ket(5)
|
|
348
|
+
t = qu.kron(a, b, c, d, parallel=parallel)
|
|
349
|
+
assert_allclose(t, a & b & c & d)
|
|
350
|
+
|
|
351
|
+
@mark.parametrize("sparse", [True, False])
|
|
352
|
+
@mark.parametrize("ri,rf", ([0, 4], [75, 89], [150, 168], [0, 168]))
|
|
353
|
+
def test_kron_ownership(self, sparse, ri, rf):
|
|
354
|
+
dims = [7, 2, 4, 3]
|
|
355
|
+
ops = [qu.rand_matrix(d, sparse=sparse) for d in dims]
|
|
356
|
+
X1 = qu.kron(*ops)[ri:rf, :]
|
|
357
|
+
X2 = qu.kron(*ops, ownership=(ri, rf))
|
|
358
|
+
assert_allclose(X1.toarray(), X2.toarray())
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
class Testikron:
|
|
362
|
+
def test_basic(self):
|
|
363
|
+
a = qu.rand_matrix(2)
|
|
364
|
+
i = qu.eye(2)
|
|
365
|
+
dims = [2, 2, 2]
|
|
366
|
+
b = qu.ikron([a], dims, [0])
|
|
367
|
+
assert_allclose(b, a & i & i)
|
|
368
|
+
b = qu.ikron([a], dims, [1])
|
|
369
|
+
assert_allclose(b, i & a & i)
|
|
370
|
+
b = qu.ikron([a], dims, [2])
|
|
371
|
+
assert_allclose(b, i & i & a)
|
|
372
|
+
b = qu.ikron([a], dims, [0, 2])
|
|
373
|
+
assert_allclose(b, a & i & a)
|
|
374
|
+
b = qu.ikron([a], dims, [0, 1, 2])
|
|
375
|
+
assert_allclose(b, a & a & a)
|
|
376
|
+
|
|
377
|
+
def test_mid_multi(self):
|
|
378
|
+
a = [qu.rand_matrix(2) for i in range(3)]
|
|
379
|
+
i = qu.eye(2)
|
|
380
|
+
dims = [2, 2, 2, 2, 2, 2]
|
|
381
|
+
inds = [1, 2, 4]
|
|
382
|
+
b = qu.ikron(a, dims, inds)
|
|
383
|
+
assert_allclose(b, i & a[0] & a[1] & i & a[2] & i)
|
|
384
|
+
|
|
385
|
+
def test_mid_multi_reverse(self):
|
|
386
|
+
a = [qu.rand_matrix(2) for i in range(3)]
|
|
387
|
+
i = qu.eye(2)
|
|
388
|
+
dims = [2, 2, 2, 2, 2, 2]
|
|
389
|
+
inds = [5, 4, 1]
|
|
390
|
+
b = qu.ikron(a, dims, inds)
|
|
391
|
+
assert_allclose(b, i & a[2] & i & i & a[1] & a[0])
|
|
392
|
+
|
|
393
|
+
def test_auto(self):
|
|
394
|
+
a = qu.rand_matrix(2)
|
|
395
|
+
i = qu.eye(2)
|
|
396
|
+
b = qu.ikron([a], (2, -1, 2), [1])
|
|
397
|
+
assert_allclose(b, i & a & i)
|
|
398
|
+
|
|
399
|
+
def test_ndarrays(self):
|
|
400
|
+
a = qu.rand_matrix(2)
|
|
401
|
+
i = qu.eye(2)
|
|
402
|
+
b = qu.ikron([a], np.array([2, 2, 2]), [0, 2])
|
|
403
|
+
assert_allclose(b, a & i & a)
|
|
404
|
+
b = qu.ikron([a], [2, 2, 2], np.array([0, 2]))
|
|
405
|
+
assert_allclose(b, a & i & a)
|
|
406
|
+
|
|
407
|
+
def test_overlap(self):
|
|
408
|
+
a = [qu.rand_matrix(4) for i in range(2)]
|
|
409
|
+
dims1 = [2, 2, 2, 2, 2, 2]
|
|
410
|
+
dims2 = [2, 4, 4, 2]
|
|
411
|
+
b = qu.ikron(a, dims1, [1, 2, 3, 4])
|
|
412
|
+
c = qu.ikron(a, dims2, [1, 2])
|
|
413
|
+
assert_allclose(c, b)
|
|
414
|
+
dims2 = [4, 2, 2, 4]
|
|
415
|
+
b = qu.ikron(a, dims1, [0, 1, 4, 5])
|
|
416
|
+
c = qu.ikron(a, dims2, [0, 3])
|
|
417
|
+
assert_allclose(c, b)
|
|
418
|
+
|
|
419
|
+
def test_holey_overlap(self):
|
|
420
|
+
a = qu.rand_matrix(8)
|
|
421
|
+
dims1 = (2, 2, 2, 2, 2)
|
|
422
|
+
dims2 = (2, 8, 2)
|
|
423
|
+
b = qu.ikron(a, dims1, (1, 3))
|
|
424
|
+
c = qu.ikron(a, dims2, 1)
|
|
425
|
+
assert_allclose(b, c)
|
|
426
|
+
dims1 = (2, 2, 2, 2, 2)
|
|
427
|
+
dims2 = (2, 2, 8)
|
|
428
|
+
b = qu.ikron(a, dims1, (2, 4))
|
|
429
|
+
c = qu.ikron(a, dims2, 2)
|
|
430
|
+
assert_allclose(b, c)
|
|
431
|
+
dims1 = (2, 2, 2, 2, 2)
|
|
432
|
+
dims2 = (8, 2, 2)
|
|
433
|
+
b = qu.ikron(a, dims1, (0, 2))
|
|
434
|
+
c = qu.ikron(a, dims2, 0)
|
|
435
|
+
assert_allclose(b, c)
|
|
436
|
+
|
|
437
|
+
def test_sparse(self):
|
|
438
|
+
i = qu.eye(2, sparse=True)
|
|
439
|
+
a = qu.qu(qu.rand_matrix(2), sparse=True)
|
|
440
|
+
b = qu.ikron(a, [2, 2, 2], 1) # infer sparse
|
|
441
|
+
assert qu.issparse(b)
|
|
442
|
+
assert_allclose(b.toarray(), (i & a & i).toarray())
|
|
443
|
+
a = qu.rand_matrix(2)
|
|
444
|
+
b = qu.ikron(a, [2, 2, 2], 1, sparse=True) # explicit sparse
|
|
445
|
+
assert qu.issparse(b)
|
|
446
|
+
assert_allclose(b.toarray(), (i & a & i).toarray())
|
|
447
|
+
|
|
448
|
+
def test_2d_simple(self):
|
|
449
|
+
a = (qu.rand_matrix(2), qu.rand_matrix(2))
|
|
450
|
+
dims = ((2, 3), (3, 2))
|
|
451
|
+
inds = ((0, 0), (1, 1))
|
|
452
|
+
b = qu.ikron(a, dims, inds)
|
|
453
|
+
assert b.shape == (36, 36)
|
|
454
|
+
assert_allclose(b, a[0] & qu.eye(9) & a[1])
|
|
455
|
+
|
|
456
|
+
@mark.parametrize("stype", (None,) + stypes)
|
|
457
|
+
@mark.parametrize(
|
|
458
|
+
"pos", [0, 1, 2, (0,), (1,), (2,), (0, 1), (1, 2), (0, 2)]
|
|
459
|
+
)
|
|
460
|
+
@mark.parametrize("coo_build", [False, True])
|
|
461
|
+
def test_sparse_format_outputs(self, os1, stype, pos, coo_build):
|
|
462
|
+
x = qu.ikron(os1, [3, 3, 3], pos, stype=stype, coo_build=coo_build)
|
|
463
|
+
assert x.format == "csr" if stype is None else stype
|
|
464
|
+
|
|
465
|
+
@mark.parametrize("stype", (None,) + stypes)
|
|
466
|
+
@mark.parametrize(
|
|
467
|
+
"pos", [0, 1, 2, (0,), (1,), (2,), (0, 1), (1, 2), (0, 2)]
|
|
468
|
+
)
|
|
469
|
+
@mark.parametrize("coo_build", [False, True])
|
|
470
|
+
def test_sparse_format_outputs_with_dense(
|
|
471
|
+
self, od1, stype, pos, coo_build
|
|
472
|
+
):
|
|
473
|
+
x = qu.ikron(
|
|
474
|
+
od1, [3, 3, 3], pos, sparse=True, stype=stype, coo_build=coo_build
|
|
475
|
+
)
|
|
476
|
+
try:
|
|
477
|
+
default = "bsr" if (2 in pos and not coo_build) else "csr"
|
|
478
|
+
except TypeError:
|
|
479
|
+
default = "bsr" if (pos == 2 and not coo_build) else "csr"
|
|
480
|
+
assert x.format == default if stype is None else stype
|
|
481
|
+
|
|
482
|
+
@mark.parametrize("sparse", [True, False])
|
|
483
|
+
@mark.parametrize("ri,rf", ([0, 4], [75, 89], [150, 168], [0, 168]))
|
|
484
|
+
def test_ikron_ownership(self, sparse, ri, rf):
|
|
485
|
+
dims = [7, 2, 4, 3]
|
|
486
|
+
X = qu.rand_matrix(2, sparse=sparse)
|
|
487
|
+
Y = qu.rand_matrix(3, sparse=sparse)
|
|
488
|
+
X1 = qu.ikron((X, Y), dims, (1, 3))[ri:rf, :]
|
|
489
|
+
X2 = qu.ikron((X, Y), dims, (1, 3), ownership=(ri, rf))
|
|
490
|
+
assert_allclose(X1.toarray(), X2.toarray())
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
class TestPermikron:
|
|
494
|
+
def test_dop_spread(self):
|
|
495
|
+
a = qu.rand_rho(4)
|
|
496
|
+
b = qu.pkron(a, [2, 2, 2], [0, 2])
|
|
497
|
+
c = (
|
|
498
|
+
(a & qu.eye(2))
|
|
499
|
+
.toarray().reshape([2, 2, 2, 2, 2, 2])
|
|
500
|
+
.transpose([0, 2, 1, 3, 5, 4])
|
|
501
|
+
.reshape([8, 8])
|
|
502
|
+
)
|
|
503
|
+
assert_allclose(b, c)
|
|
504
|
+
|
|
505
|
+
def test_dop_reverse(self):
|
|
506
|
+
a = qu.rand_rho(4)
|
|
507
|
+
b = qu.pkron(a, np.array([2, 2, 2]), [2, 0])
|
|
508
|
+
c = (
|
|
509
|
+
(a & qu.eye(2))
|
|
510
|
+
.toarray().reshape([2, 2, 2, 2, 2, 2])
|
|
511
|
+
.transpose([1, 2, 0, 4, 5, 3])
|
|
512
|
+
.reshape([8, 8])
|
|
513
|
+
)
|
|
514
|
+
assert_allclose(b, c)
|
|
515
|
+
|
|
516
|
+
def test_dop_reverse_sparse(self):
|
|
517
|
+
a = qu.rand_rho(4, sparse=True, density=0.5)
|
|
518
|
+
b = qu.pkron(a, np.array([2, 2, 2]), [2, 0])
|
|
519
|
+
c = (
|
|
520
|
+
(a & qu.eye(2))
|
|
521
|
+
.toarray().reshape([2, 2, 2, 2, 2, 2])
|
|
522
|
+
.transpose([1, 2, 0, 4, 5, 3])
|
|
523
|
+
.reshape([8, 8])
|
|
524
|
+
)
|
|
525
|
+
assert_allclose(b.toarray(), c)
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
class TestPermute:
|
|
529
|
+
def test_permute_ket(self):
|
|
530
|
+
a = qu.up() & qu.plus() & qu.yplus()
|
|
531
|
+
b = qu.permute(a, [2, 2, 2], [2, 0, 1])
|
|
532
|
+
assert_allclose(b, qu.yplus() & qu.up() & qu.plus())
|
|
533
|
+
|
|
534
|
+
def test_permute_op(self):
|
|
535
|
+
a = qu.pauli("x") & qu.pauli("y") & qu.pauli("z")
|
|
536
|
+
b = qu.permute(a, [2, 2, 2], [2, 0, 1])
|
|
537
|
+
assert_allclose(b, qu.pauli("z") & qu.pauli("x") & qu.pauli("y"))
|
|
538
|
+
|
|
539
|
+
def test_entangled_permute(self):
|
|
540
|
+
dims = [2, 2, 2]
|
|
541
|
+
a = qu.bell_state(0) & qu.up()
|
|
542
|
+
assert_allclose(qu.mutinf_subsys(a, dims, 0, 1), 2.0)
|
|
543
|
+
b = qu.permute(a, dims, [1, 2, 0])
|
|
544
|
+
assert_allclose(qu.mutinf_subsys(b, dims, 0, 1), 0.0, atol=1e-12)
|
|
545
|
+
assert_allclose(qu.mutinf_subsys(b, dims, 0, 2), 2.0)
|
|
546
|
+
|
|
547
|
+
def test_permute_sparse_ket(self):
|
|
548
|
+
dims = [3, 2, 5, 4]
|
|
549
|
+
a = qu.rand_ket(qu.prod(dims), sparse=True, density=0.5)
|
|
550
|
+
b = qu.permute(a, dims, [3, 1, 2, 0])
|
|
551
|
+
c = qu.permute(a.toarray(), dims, [3, 1, 2, 0])
|
|
552
|
+
assert_allclose(b.toarray(), c)
|
|
553
|
+
|
|
554
|
+
def test_permute_sparse_op(self):
|
|
555
|
+
dims = [3, 2, 5, 4]
|
|
556
|
+
a = qu.rand_rho(qu.prod(dims), sparse=True, density=0.5)
|
|
557
|
+
b = qu.permute(a, dims, [3, 1, 2, 0])
|
|
558
|
+
c = qu.permute(a.toarray(), dims, [3, 1, 2, 0])
|
|
559
|
+
assert_allclose(b.toarray(), c)
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
class TestPartialTraceDense:
|
|
563
|
+
def test_partial_trace_basic(self):
|
|
564
|
+
a = qu.rand_rho(2**2)
|
|
565
|
+
b = qu.partial_trace(a, [2, 2], 0)
|
|
566
|
+
assert isinstance(b, qu.qarray)
|
|
567
|
+
assert qu.isherm(b)
|
|
568
|
+
assert_allclose(qu.tr(b), 1.0)
|
|
569
|
+
|
|
570
|
+
def test_ptr_compare_to_manual(self):
|
|
571
|
+
a = qu.rand_rho(2**2)
|
|
572
|
+
b = qu.partial_trace(a, [2, 2], 0)
|
|
573
|
+
c = a.toarray().reshape([2, 2, 2, 2]).trace(axis1=1, axis2=3)
|
|
574
|
+
assert_allclose(b, c)
|
|
575
|
+
b = qu.partial_trace(a, [2, 2], 1)
|
|
576
|
+
c = a.toarray().reshape([2, 2, 2, 2]).trace(axis1=0, axis2=2)
|
|
577
|
+
assert_allclose(b, c)
|
|
578
|
+
|
|
579
|
+
def test_partial_trace_early_return(self):
|
|
580
|
+
a = qu.qu([0.5, 0.5, 0.5, 0.5], "ket")
|
|
581
|
+
b = qu.partial_trace(a, [2, 2], [0, 1])
|
|
582
|
+
assert_allclose(a @ a.H, b)
|
|
583
|
+
a = qu.qu([0.5, 0.5, 0.5, 0.5], "dop")
|
|
584
|
+
b = qu.partial_trace(a, [2, 2], [0, 1])
|
|
585
|
+
assert_allclose(a, b)
|
|
586
|
+
|
|
587
|
+
def test_partial_trace_return_type(self):
|
|
588
|
+
a = qu.qu([0, 2**-0.5, 2**-0.5, 0], "ket")
|
|
589
|
+
b = qu.partial_trace(a, [2, 2], 1)
|
|
590
|
+
assert type(b) == qu.qarray
|
|
591
|
+
a = qu.qu([0, 2**-0.5, 2**-0.5, 0], "dop")
|
|
592
|
+
b = qu.partial_trace(a, [2, 2], 1)
|
|
593
|
+
assert type(b) == qu.qarray
|
|
594
|
+
|
|
595
|
+
def test_partial_trace_single_ket(self):
|
|
596
|
+
dims = [2, 3, 4]
|
|
597
|
+
a = np.random.randn(qu.prod(dims), 1)
|
|
598
|
+
for i, dim in enumerate(dims):
|
|
599
|
+
b = qu.partial_trace(a, dims, i)
|
|
600
|
+
assert b.shape[0] == dim
|
|
601
|
+
|
|
602
|
+
def test_partial_trace_multi_ket(self):
|
|
603
|
+
dims = [2, 3, 4]
|
|
604
|
+
a = np.random.randn(qu.prod(dims), 1)
|
|
605
|
+
for i1, i2 in itertools.combinations([0, 1, 2], 2):
|
|
606
|
+
b = qu.partial_trace(a, dims, [i1, i2])
|
|
607
|
+
assert b.shape[1] == dims[i1] * dims[i2]
|
|
608
|
+
|
|
609
|
+
def test_partial_trace_dop_product_state(self):
|
|
610
|
+
dims = [3, 2, 4, 2, 3]
|
|
611
|
+
ps = [qu.rand_rho(dim) for dim in dims]
|
|
612
|
+
pt = qu.kron(*ps)
|
|
613
|
+
for i, dim in enumerate(dims):
|
|
614
|
+
p = qu.partial_trace(pt, dims, i)
|
|
615
|
+
assert_allclose(p, ps[i])
|
|
616
|
+
|
|
617
|
+
def test_partial_trace_bell_states(self):
|
|
618
|
+
for lab in ("psi-", "psi+", "phi-", "phi+"):
|
|
619
|
+
psi = qu.bell_state(lab, qtype="dop")
|
|
620
|
+
rhoa = qu.partial_trace(psi, [2, 2], 0)
|
|
621
|
+
assert_allclose(rhoa, qu.eye(2) / 2)
|
|
622
|
+
|
|
623
|
+
def test_partial_trace_supply_ndarray(self):
|
|
624
|
+
a = qu.rand_rho(2**3)
|
|
625
|
+
dims = np.array([2, 2, 2])
|
|
626
|
+
keep = np.array(1)
|
|
627
|
+
b = qu.partial_trace(a, dims, keep)
|
|
628
|
+
assert b.shape[0] == 2
|
|
629
|
+
|
|
630
|
+
def test_partial_trace_order_doesnt_matter(self):
|
|
631
|
+
a = qu.rand_rho(2**3)
|
|
632
|
+
dims = np.array([2, 2, 2])
|
|
633
|
+
b1 = qu.partial_trace(a, dims, [0, 2])
|
|
634
|
+
b2 = qu.partial_trace(a, dims, [2, 0])
|
|
635
|
+
assert_allclose(b1, b2)
|
|
636
|
+
|
|
637
|
+
|
|
638
|
+
class TestTraceLose:
|
|
639
|
+
def test_rps(self):
|
|
640
|
+
a, b, c = (
|
|
641
|
+
qu.rand_rho(2, sparse=True, density=0.5),
|
|
642
|
+
qu.rand_rho(3, sparse=True, density=0.5),
|
|
643
|
+
qu.rand_rho(2, sparse=True, density=0.5),
|
|
644
|
+
)
|
|
645
|
+
abc = a & b & c
|
|
646
|
+
pab = qu.core._trace_lose(abc, [2, 3, 2], 2)
|
|
647
|
+
assert_allclose(pab, (a & b).toarray())
|
|
648
|
+
pac = qu.core._trace_lose(abc, [2, 3, 2], 1)
|
|
649
|
+
assert_allclose(pac, (a & c).toarray())
|
|
650
|
+
pbc = qu.core._trace_lose(abc, [2, 3, 2], 0)
|
|
651
|
+
assert_allclose(pbc, (b & c).toarray())
|
|
652
|
+
|
|
653
|
+
def test_bell_state(self):
|
|
654
|
+
a = qu.bell_state("psi-", sparse=True)
|
|
655
|
+
b = qu.core._trace_lose(a @ a.H, [2, 2], 0)
|
|
656
|
+
assert_allclose(b, qu.eye(2) / 2)
|
|
657
|
+
b = qu.core._trace_lose(a @ a.H, [2, 2], 1)
|
|
658
|
+
assert_allclose(b, qu.eye(2) / 2)
|
|
659
|
+
|
|
660
|
+
def test_vs_ptr(self):
|
|
661
|
+
a = qu.rand_rho(6, sparse=True, density=0.5)
|
|
662
|
+
b = qu.core._trace_lose(a, [2, 3], 1)
|
|
663
|
+
c = qu.partial_trace(a.toarray(), [2, 3], 0)
|
|
664
|
+
assert_allclose(b, c)
|
|
665
|
+
b = qu.core._trace_lose(a, [2, 3], 0)
|
|
666
|
+
c = qu.partial_trace(a.toarray(), [2, 3], 1)
|
|
667
|
+
assert_allclose(b, c)
|
|
668
|
+
|
|
669
|
+
def test_vec_dense(self):
|
|
670
|
+
a = qu.rand_ket(4)
|
|
671
|
+
b = qu.core._trace_lose(a, [2, 2], 1)
|
|
672
|
+
c = qu.partial_trace(a.toarray(), [2, 2], 0)
|
|
673
|
+
assert_allclose(b, c)
|
|
674
|
+
b = qu.core._trace_lose(a, [2, 2], 0)
|
|
675
|
+
c = qu.partial_trace(a.toarray(), [2, 2], 1)
|
|
676
|
+
assert_allclose(b, c)
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
class TestTraceKeep:
|
|
680
|
+
def test_rps(self):
|
|
681
|
+
a, b, c = (
|
|
682
|
+
qu.rand_rho(2, sparse=True, density=0.5),
|
|
683
|
+
qu.rand_rho(3, sparse=True, density=0.5),
|
|
684
|
+
qu.rand_rho(2, sparse=True, density=0.5),
|
|
685
|
+
)
|
|
686
|
+
abc = a & b & c
|
|
687
|
+
pc = qu.core._trace_keep(abc, [2, 3, 2], 2)
|
|
688
|
+
assert_allclose(pc, c.toarray())
|
|
689
|
+
pb = qu.core._trace_keep(abc, [2, 3, 2], 1)
|
|
690
|
+
assert_allclose(pb, b.toarray())
|
|
691
|
+
pa = qu.core._trace_keep(abc, [2, 3, 2], 0)
|
|
692
|
+
assert_allclose(pa, a.toarray())
|
|
693
|
+
|
|
694
|
+
def test_bell_state(self):
|
|
695
|
+
a = qu.bell_state("psi-", sparse=True)
|
|
696
|
+
b = qu.core._trace_keep(a @ a.H, [2, 2], 0)
|
|
697
|
+
assert_allclose(b, qu.eye(2) / 2)
|
|
698
|
+
b = qu.core._trace_keep(a @ a.H, [2, 2], 1)
|
|
699
|
+
assert_allclose(b, qu.eye(2) / 2)
|
|
700
|
+
|
|
701
|
+
def test_vs_ptr(self):
|
|
702
|
+
a = qu.rand_rho(6, sparse=True, density=0.5)
|
|
703
|
+
b = qu.core._trace_keep(a, [2, 3], 0)
|
|
704
|
+
c = qu.partial_trace(a.toarray(), [2, 3], 0)
|
|
705
|
+
assert_allclose(b, c)
|
|
706
|
+
b = qu.core._trace_keep(a, [2, 3], 1)
|
|
707
|
+
c = qu.partial_trace(a.toarray(), [2, 3], 1)
|
|
708
|
+
assert_allclose(b, c)
|
|
709
|
+
|
|
710
|
+
def test_vec_dense(self):
|
|
711
|
+
a = qu.rand_ket(4)
|
|
712
|
+
b = qu.core._trace_keep(a, [2, 2], 0)
|
|
713
|
+
c = qu.partial_trace(a.toarray(), [2, 2], 0)
|
|
714
|
+
assert_allclose(b, c)
|
|
715
|
+
b = qu.core._trace_keep(a, [2, 2], 1)
|
|
716
|
+
c = qu.partial_trace(a.toarray(), [2, 2], 1)
|
|
717
|
+
assert_allclose(b, c)
|
|
718
|
+
|
|
719
|
+
|
|
720
|
+
class TestPartialTraceSparse:
|
|
721
|
+
def test_partial_trace_sparse_basic(self):
|
|
722
|
+
a = qu.rand_rho(4)
|
|
723
|
+
b = qu.partial_trace(a, [2, 2], 0)
|
|
724
|
+
assert type(b) == qu.qarray
|
|
725
|
+
assert qu.isherm(b)
|
|
726
|
+
assert_allclose(qu.tr(b), 1.0)
|
|
727
|
+
|
|
728
|
+
def test_partial_trace_simple_single(self):
|
|
729
|
+
a = qu.rand_rho(12, sparse=True, density=0.5)
|
|
730
|
+
dims = [2, 3, 2]
|
|
731
|
+
b = qu.partial_trace(a, dims, 1)
|
|
732
|
+
c = (
|
|
733
|
+
a.toarray().reshape([*dims, *dims])
|
|
734
|
+
.trace(axis1=2, axis2=5)
|
|
735
|
+
.trace(axis1=0, axis2=2)
|
|
736
|
+
)
|
|
737
|
+
assert_allclose(c, b)
|
|
738
|
+
|
|
739
|
+
def test_partial_trace_simple_double(self):
|
|
740
|
+
a = qu.rand_rho(12, sparse=True, density=0.5)
|
|
741
|
+
dims = [2, 3, 2]
|
|
742
|
+
b = qu.partial_trace(a, dims, [0, 2])
|
|
743
|
+
c = qu.partial_trace(a.toarray(), dims, [0, 2])
|
|
744
|
+
assert_allclose(b, c)
|
|
745
|
+
b = qu.partial_trace(a, dims, [1, 2])
|
|
746
|
+
c = qu.partial_trace(a.toarray(), dims, [1, 2])
|
|
747
|
+
assert_allclose(b, c)
|
|
748
|
+
|
|
749
|
+
def test_partial_trace_simple_ket(self):
|
|
750
|
+
a = qu.rand_ket(12, sparse=True, density=0.5)
|
|
751
|
+
dims = [2, 3, 2]
|
|
752
|
+
b = qu.partial_trace(a, dims, [0, 1])
|
|
753
|
+
c = qu.partial_trace(a.toarray(), dims, [0, 1])
|
|
754
|
+
assert_allclose(b, c)
|
|
755
|
+
|
|
756
|
+
|
|
757
|
+
class TestChop:
|
|
758
|
+
def test_chop_inplace(self):
|
|
759
|
+
a = qu.qu([-1j, 0.1 + 0.2j])
|
|
760
|
+
qu.chop(a, tol=0.11, inplace=True)
|
|
761
|
+
assert_allclose(a, qu.qu([-1j, 0.2j]))
|
|
762
|
+
# Sparse
|
|
763
|
+
a = qu.qu([-1j, 0.1 + 0.2j], sparse=True)
|
|
764
|
+
qu.chop(a, tol=0.11, inplace=True)
|
|
765
|
+
b = qu.qu([-1j, 0.2j], sparse=True)
|
|
766
|
+
assert (a != b).nnz == 0
|
|
767
|
+
|
|
768
|
+
def test_chop_inplace_dop(self):
|
|
769
|
+
a = qu.qu([1, 0.1], "dop")
|
|
770
|
+
qu.chop(a, tol=0.11, inplace=True)
|
|
771
|
+
assert_allclose(a, qu.qu([1, 0], "dop"))
|
|
772
|
+
a = qu.qu([1, 0.1], "dop", sparse=True)
|
|
773
|
+
qu.chop(a, tol=0.11, inplace=True)
|
|
774
|
+
b = qu.qu([1, 0.0], "dop", sparse=True)
|
|
775
|
+
assert (a != b).nnz == 0
|
|
776
|
+
|
|
777
|
+
def test_chop_copy(self):
|
|
778
|
+
a = qu.qu([-1j, 0.1 + 0.2j])
|
|
779
|
+
b = qu.chop(a, tol=0.11, inplace=False)
|
|
780
|
+
assert_allclose(a, qu.qu([-1j, 0.1 + 0.2j]))
|
|
781
|
+
assert_allclose(b, qu.qu([-1j, 0.2j]))
|
|
782
|
+
# Sparse
|
|
783
|
+
a = qu.qu([-1j, 0.1 + 0.2j], sparse=True)
|
|
784
|
+
b = qu.chop(a, tol=0.11, inplace=False)
|
|
785
|
+
ao = qu.qu([-1j, 0.1 + 0.2j], sparse=True)
|
|
786
|
+
bo = qu.qu([-1j, 0.2j], sparse=True)
|
|
787
|
+
assert (a != ao).nnz == 0
|
|
788
|
+
assert (b != bo).nnz == 0
|
|
789
|
+
|
|
790
|
+
|
|
791
|
+
class TestExpec:
|
|
792
|
+
@mark.parametrize("qtype1", ["ket", "dop"])
|
|
793
|
+
@mark.parametrize("spars1", [True, False])
|
|
794
|
+
@mark.parametrize("qtype2", ["ket", "dop"])
|
|
795
|
+
@mark.parametrize("spars2", [True, False])
|
|
796
|
+
def test_all(self, qtype1, spars1, qtype2, spars2):
|
|
797
|
+
a = qu.qu([[1], [2j], [3]], qtype=qtype1, sparse=spars1)
|
|
798
|
+
b = qu.qu([[1j], [2], [3j]], qtype=qtype2, sparse=spars2)
|
|
799
|
+
c = qu.expec(a, b)
|
|
800
|
+
assert not isinstance(c, complex)
|
|
801
|
+
assert_allclose(c, 36)
|
|
802
|
+
|
|
803
|
+
@mark.parametrize("qtype", ["ket", "dop"])
|
|
804
|
+
@mark.parametrize("sparse", [True, False])
|
|
805
|
+
@mark.parametrize("s", ["x", "y", "z"])
|
|
806
|
+
def test_negative_expec(self, qtype, sparse, s):
|
|
807
|
+
a = qu.singlet(qtype=qtype)
|
|
808
|
+
b = qu.pauli(s, sparse=sparse) & qu.pauli(s, sparse=sparse)
|
|
809
|
+
assert_allclose(qu.expec(a, b), -1)
|
|
810
|
+
|
|
811
|
+
|
|
812
|
+
class TestNumbaFuncs:
|
|
813
|
+
@mark.parametrize("size", [300, 3000, (300, 5), (3000, 5)])
|
|
814
|
+
@mark.parametrize(
|
|
815
|
+
"X_dtype", ["float32", "float64", "complex64", "complex128"]
|
|
816
|
+
)
|
|
817
|
+
@mark.parametrize("c_dtype", ["float32", "float64"])
|
|
818
|
+
def test_subtract_update(
|
|
819
|
+
self,
|
|
820
|
+
size,
|
|
821
|
+
X_dtype,
|
|
822
|
+
c_dtype,
|
|
823
|
+
):
|
|
824
|
+
X = qu.randn(size, dtype=X_dtype)
|
|
825
|
+
Y = qu.randn(size, dtype=X_dtype)
|
|
826
|
+
c = qu.randn(1, dtype=c_dtype)[0]
|
|
827
|
+
res = X - c * Y
|
|
828
|
+
qu.core.subtract_update_(X, c, Y)
|
|
829
|
+
assert_allclose(res, X, rtol=1e-6)
|
|
830
|
+
|
|
831
|
+
@mark.parametrize("size", [300, 3000, (300, 5), (3000, 5)])
|
|
832
|
+
@mark.parametrize(
|
|
833
|
+
"X_dtype", ["float32", "float64", "complex64", "complex128"]
|
|
834
|
+
)
|
|
835
|
+
@mark.parametrize("c_dtype", ["float32", "float64"])
|
|
836
|
+
def test_divide_update(
|
|
837
|
+
self,
|
|
838
|
+
size,
|
|
839
|
+
X_dtype,
|
|
840
|
+
c_dtype,
|
|
841
|
+
):
|
|
842
|
+
X = qu.randn(size, dtype=X_dtype)
|
|
843
|
+
Y = np.empty_like(X)
|
|
844
|
+
c = qu.randn(1, dtype=c_dtype).item()
|
|
845
|
+
res = X / c
|
|
846
|
+
qu.core.divide_update_(X, c, Y)
|
|
847
|
+
assert_allclose(res, Y, rtol=1e-6)
|