Repository navigation
Expand file tree
/
Copy pathtest_tensor_train.py
More file actions
221 lines (150 loc) · 6.48 KB
/
Copy pathtest_tensor_train.py
File metadata and controls
221 lines (150 loc) · 6.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
"""Test the stack helpers in `src_method._tensor_train`."""
import numpy as np
import pytest
from src_method._tensor_train import (
SwappedLegs,
exact_stack,
normalize_stack,
pad,
pad_site,
padded_shape,
transpose_mpo,
unpad,
)
def random_mpo(n_sites, bond, rng, *, up=2, down=2):
return (
[rng.normal(size=(bond, up, down))]
+ [rng.normal(size=(bond, bond, up, down)) for _ in range(n_sites - 2)]
+ [rng.normal(size=(bond, up, down))]
)
def random_mps(n_sites, bond, rng, *, phys=2):
return (
[rng.normal(size=(bond, phys))]
+ [rng.normal(size=(bond, bond, phys)) for _ in range(n_sites - 2)]
+ [rng.normal(size=(bond, phys))]
)
@pytest.fixture
def rng():
return np.random.default_rng(0)
# ------------------------
# --- pad / unpad -------
# ------------------------
@pytest.mark.parametrize("kind", ["mps", "mpo"])
def test_pad_shapes_and_round_trip(kind, rng):
train = random_mps(4, 3, rng) if kind == "mps" else random_mpo(4, 3, rng)
padded = pad(train)
down = 1 if kind == "mps" else 2
assert [t.shape for t in padded] == [
(1, 3, 2, down),
(3, 3, 2, down),
(3, 3, 2, down),
(3, 1, 2, down),
]
for original, restored in zip(train, unpad(padded, kind)):
np.testing.assert_array_equal(original, restored)
def test_pad_does_not_copy(rng):
train = random_mpo(3, 2, rng)
assert all(np.shares_memory(t, p) for t, p in zip(train, pad(train)))
def test_transpose_mpo_swaps_physical_legs(rng):
train = random_mpo(3, 2, rng, up=2, down=3)
transposed = transpose_mpo(train)
assert [t.shape for t in transposed] == [(2, 3, 2), (2, 2, 3, 2), (2, 3, 2)]
np.testing.assert_array_equal(transposed[1], train[1].transpose(0, 1, 3, 2))
# ----------------------------
# --- normalize_stack -------
# ----------------------------
def test_normalize_single_trains(rng):
mps, mpo = random_mps(3, 2, rng), random_mpo(3, 2, rng)
assert normalize_stack([mps]) == ([mps], "mps")
assert normalize_stack([mpo]) == ([mpo], "mpo")
def test_normalize_ket_stack_is_unchanged(rng):
A, B, psi = random_mpo(3, 2, rng), random_mpo(3, 2, rng), random_mps(3, 2, rng)
assert normalize_stack([A, B]) == ([A, B], "mpo")
assert normalize_stack([A, B, psi]) == ([A, B, psi], "mps")
def test_normalize_bra_stack_becomes_transposed_ket(rng):
phi = random_mps(3, 2, rng, phys=2)
A = random_mpo(3, 2, rng, up=2, down=3)
B = random_mpo(3, 2, rng, up=3, down=4)
layers, kind = normalize_stack([phi, A, B])
assert kind == "mps"
assert len(layers) == 3
for got, want in zip(layers[0], transpose_mpo(B)):
np.testing.assert_array_equal(got, want)
for got, want in zip(layers[1], transpose_mpo(A)):
np.testing.assert_array_equal(got, want)
assert layers[2] is phi
def test_normalize_empty_stack_raises():
with pytest.raises(ValueError, match="at least one tensor train"):
normalize_stack([])
def test_normalize_unknown_layout_raises(rng):
peps_like = [np.zeros((2, 2, 2, 2))] * 3
with pytest.raises(TypeError, match="layout for train 1"):
normalize_stack([random_mpo(3, 2, rng), peps_like])
@pytest.mark.parametrize(
"roles",
[("mps", "mps"), ("mpo", "mps", "mpo"), ("mps", "mpo", "mps")],
)
def test_normalize_misplaced_mps_raises(roles, rng):
trains = [
random_mps(3, 2, rng) if r == "mps" else random_mpo(3, 2, rng) for r in roles
]
with pytest.raises(TypeError, match="MPS may come first"):
normalize_stack(trains)
def test_normalize_site_count_mismatch_raises(rng):
with pytest.raises(ValueError, match="same number of sites"):
normalize_stack([random_mpo(4, 2, rng), random_mps(6, 2, rng)])
def test_normalize_physical_dim_mismatch_raises(rng):
A = random_mpo(3, 2, rng, up=2, down=3)
psi = random_mps(3, 2, rng, phys=2)
with pytest.raises(ValueError, match="site 0 between trains 0 and 1: 3 != 2"):
normalize_stack([A, psi])
def test_normalize_bra_physical_dim_mismatch_raises(rng):
phi = random_mps(3, 2, rng, phys=3)
A = random_mpo(3, 2, rng, up=2, down=2)
with pytest.raises(ValueError, match="between trains 0 and 1: 3 != 2"):
normalize_stack([phi, A])
# ------------------------
# --- exact_stack -------
# ------------------------
def dense_two_site(train):
"""Two-site train -> (U, D) matrix; an MPS gives a column (U, 1)."""
if train[0].ndim == 2:
return np.einsum("au,av->uv", *train).reshape(-1, 1)
first, last = train
t = np.einsum("aij,akl->ikjl", first, last)
return t.reshape(first.shape[1] * last.shape[1], -1)
@pytest.mark.parametrize("kind", ["mps", "mpo"])
def test_exact_stack_depth_three(kind, rng):
A, B = random_mpo(2, 3, rng), random_mpo(2, 2, rng)
last = random_mps(2, 2, rng) if kind == "mps" else random_mpo(2, 2, rng)
out = exact_stack([A, B, last], chi_out=64, kind=kind)
want = dense_two_site(A) @ dense_two_site(B) @ dense_two_site(last)
np.testing.assert_allclose(dense_two_site(out), want, atol=1e-10)
# -------------------------------
# --- padded_shape / pad_site ---
# -------------------------------
@pytest.mark.parametrize("kind", ["mps", "mpo"])
@pytest.mark.parametrize("n_sites", [2, 3, 4])
def test_padded_shape_matches_pad(kind, n_sites, rng):
train = (
random_mps(n_sites, 3, rng) if kind == "mps" else random_mpo(n_sites, 3, rng)
)
last = n_sites - 1
shapes = [padded_shape(t.shape, kind, i, last) for i, t in enumerate(train)]
assert shapes == [t.shape for t in pad(train)]
for i, site in enumerate(train):
np.testing.assert_array_equal(pad_site(site, kind, i, last), pad(train)[i])
class LazySite:
"""A site with shape and dtype but no array methods, read by ``np.asarray``."""
def __init__(self, data):
self.data = data
self.shape, self.dtype, self.ndim = data.shape, data.dtype, data.ndim
def __array__(self, dtype=None, copy=None):
return np.asarray(self.data, dtype=dtype)
def test_transpose_mpo_of_lazy_sites(rng):
train = random_mpo(3, 2, rng, up=2, down=3)
transposed = transpose_mpo([LazySite(site) for site in train])
assert all(isinstance(site, SwappedLegs) for site in transposed)
assert [t.shape for t in transposed] == [(2, 3, 2), (2, 2, 3, 2), (2, 3, 2)]
np.testing.assert_array_equal(np.asarray(transposed[1]), train[1].swapaxes(-2, -1))
assert np.asarray(transposed[0], dtype=np.complex64).dtype == np.complex64