Skip to content

Commit 55b06f2

Browse files
committed
experimental (new) version of La shows improvement
1 parent 645978e commit 55b06f2

5 files changed

Lines changed: 419 additions & 8 deletions

File tree

experimental/La_compiled.py

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
"""
2+
The compilation woth torch.compile requires that I use all real numbers to be effective.
3+
This file contains a minimal implementation of the canonical momenta,
4+
for a function f(U) that is non-trivial in gauge configuration of links.
5+
The latter is casted into real by extending the last dimension to contain real and imaginary part
6+
7+
"""
8+
9+
import sys
10+
sys.path.append("../../")
11+
12+
import time
13+
import torch
14+
import typing
15+
#import warnings
16+
#warnings.filterwarnings("always")
17+
import torch._dynamo
18+
torch._dynamo.config.verbose = True # prints every graph break with reason
19+
20+
21+
from lattice_data_tools.links.configuration import GaugeConfiguration
22+
import lattice_data_tools.links.suN as suN
23+
from lattice_data_tools.autodifferentiation.with_torch_func_grad import get_compiled_function
24+
25+
26+
def complex2ri(x):
27+
return torch.stack([x.real,x.imag], dim=-1)
28+
29+
def adjoint(M):
30+
M_T = torch.transpose(M, -3, -2)
31+
M_H = torch.stack([M_T[...,0], -M_T[...,1]], dim=-1)
32+
return M_H
33+
34+
def complex_matmul(A, B):
35+
ReA, ImA = A[..., 0], A[..., 1]
36+
ReB, ImB = B[..., 0], B[..., 1]
37+
Re_AB = ReA@ ReB - ImA @ ImB
38+
Im_AB = ReA@ ImB + ImA @ ReB
39+
AB = torch.stack([Re_AB, Im_AB], dim=-1)
40+
return AB
41+
42+
43+
class La_Generator:
44+
"""
45+
Object generating the momenta L_a, for each a and each link in the configuration, and each configuration
46+
47+
The output is a lambda function (potentially compiled with `toch.compile`), which takes U in the usual shape (it is flattened iternally)
48+
49+
"""
50+
def __init__(self, f: typing.Callable, U: GaugeConfiguration, do_compile: bool):
51+
"""
52+
Initialize the lambda function for the La^2,
53+
using the function,
54+
an example gauge configuration (for the number of links). U.shape==(batchsize, n_links)
55+
and when `do_compile==True` it is compiled
56+
"""
57+
#assert(len(U.shape) == 2) # (batchsize, n_var)
58+
batchsize = U.shape[0] # number of configurations
59+
n_links = U.n_links # number of links
60+
Nc = U.Nc # number of colors
61+
Ng = U.Ng # number of generators of the Lie algebra
62+
lattice_shape = U.lattice_shape
63+
device = U.device
64+
dtype = U.dtype
65+
66+
# diagonalization of the tau_a
67+
tau = suN.get_generators(Nc=Nc, device=device, dtype=dtype)
68+
tau_eigh_complex = torch.linalg.eigh(tau)
69+
tau_eigh = (tau_eigh_complex[0], complex2ri(tau_eigh_complex[1]))
70+
71+
Id = torch.stack(
72+
[
73+
torch.eye(Nc, device=U.device, dtype=U.real.dtype),
74+
torch.zeros(Nc, Nc, device=U.device, dtype=U.real.dtype)
75+
], dim=-1)
76+
Id_arr = Id.expand(n_links, Nc, Nc, 2)
77+
single_conf_shape = (1,)+U.shape[1:]+(2,)
78+
def f_shift(tau_a_eigh, Ub, eps, i):
79+
# d, M = tau_eigh[a]
80+
d, M = tau_a_eigh # torch.linalg.eigh(tau_a)
81+
exp_iD = torch.stack(
82+
[
83+
torch.diag_embed(torch.cos(eps * d)),
84+
torch.diag_embed(torch.sin(eps * d))
85+
],
86+
dim=-1)
87+
Va = complex_matmul(complex_matmul(M, exp_iD), adjoint(M))
88+
ei = (torch.arange(n_links, device=Ub.device) == i).to(Ub.dtype)
89+
Va_arr = Id_arr + torch.einsum("abC,i->iabC", Va-Id, ei)
90+
VaU_i = complex_matmul(Va_arr, Ub).reshape(*single_conf_shape)
91+
res = f(VaU_i) # shape==(1,1)
92+
return res[0,:]
93+
def Re_f_shift(tau_a_eigh, Ub, eps, i):
94+
return f_shift(tau_a_eigh, Ub, eps, i)[0]
95+
def Im_f_shift(tau_a_eigh, Ub, eps, i):
96+
return f_shift(tau_a_eigh, Ub, eps, i)[1]
97+
98+
Re_df = torch.func.grad(Re_f_shift, argnums=2) # abstract gradient object
99+
Im_df = torch.func.grad(Im_f_shift, argnums=2) # abstract gradient object
100+
101+
eps = torch.tensor(0.0, device=device, dtype=U.real.dtype)
102+
idx_links = torch.arange(n_links, device=device)
103+
# idx_generators = torch.arange(Ng, device=device)
104+
La_f_shape = (batchsize,Ng,*(U.shape[1:-2]))
105+
106+
def get_compiled(df_i):
107+
# vmap can parallelize only along dimension with the same size
108+
df_i_vmapped = torch.func.vmap(
109+
torch.func.vmap(
110+
torch.func.vmap(
111+
df_i,
112+
in_dims=(None,None,None, 0) # parallelizing only over the variable index --> \\partial_{eps_i}^2 f(V_eps @ U)
113+
),
114+
in_dims=(0,None,None,None) # parallelize along generators a=1,...,Ng
115+
),
116+
in_dims=(None,0,None,None), # parallelizing only over the batch index f(x1^{i}, x2^{i},...)
117+
)
118+
# df_i_vmapped = torch.func.vmap( # over batch
119+
# torch.func.vmap( # over generators
120+
# torch.func.vmap(df_i, # over link index i
121+
# in_dims=(None, None, None, 0)
122+
# ),
123+
# in_dims=(0, None, None, None)
124+
# ),
125+
# in_dims=(None, 0, None, None)
126+
# )
127+
128+
def uncompiled_df(U_arr):
129+
U_flat = U_arr.view(batchsize,-1,Nc,Nc,2)
130+
res = df_i_vmapped(tau_eigh, U_flat, eps, idx_links)
131+
return res.reshape(La_f_shape)
132+
133+
U_tens = complex2ri(U.as_subclass(torch.Tensor))
134+
dummy = uncompiled_df(U_tens)
135+
if do_compile:
136+
compiled_df_i = get_compiled_function(uncompiled_df, U_tens)
137+
else:
138+
compiled_df_i = uncompiled_df
139+
#---
140+
return compiled_df_i
141+
142+
compiled_Re_df = get_compiled(Re_df)
143+
compiled_Im_df = get_compiled(Im_df)
144+
# including the factor "i": -i*d/d\\epsilon
145+
self._df_function = lambda U: -1j*(compiled_Re_df(U) + 1j*compiled_Im_df(U))
146+
147+
@property
148+
def df_function(self):
149+
return self._df_function
150+
151+
152+
153+
154+
def perf(fun, info: str):
155+
torch.cuda.synchronize()
156+
t1 = time.time()
157+
res = fun()
158+
torch.cuda.synchronize()
159+
t2 = time.time()
160+
print(f"dt ({info}): {t2-t1} sec.")
161+
return res
162+
163+
164+
165+
166+
def f(U_ri):
167+
n = len(U_ri.shape)
168+
# U_xp1 = torch.roll(U_ri, shifts=1, dims=0)
169+
# res = (U_xp1*U_ri).sum(dim=tuple(torch.arange(1,n-1)))
170+
res = (U_ri).sum(dim=tuple(torch.arange(1,n-1)))
171+
return res
172+
173+
B = 5
174+
L_mu = [2,2]
175+
Nc = 3
176+
#device = torch.device("cpu")
177+
device = torch.device("cpu")
178+
179+
U = GaugeConfiguration.from_hotstart(
180+
batchsize=B, L_mu=L_mu, Nc=Nc,
181+
seed=20260501, dtype=torch.complex128, device=device,
182+
requires_grad=False
183+
)
184+
185+
U_tens = U.as_subclass(torch.Tensor)
186+
U_ri = complex2ri(U_tens)
187+
188+
189+
LaG = La_Generator(f=f, U=U, do_compile=False)
190+
191+
N = 10
192+
193+
t0 = time.time()
194+
for i in range(N):
195+
_ = LaG.df_function(U=U_ri)
196+
t1 = time.time()
197+
print((t1-t0)/N)
198+
199+
LaG_compiled = La_Generator(f=f, U=U, do_compile=True)
200+
t0 = time.time()
201+
for i in range(N):
202+
_ = LaG_compiled.df_function(U=U_ri)
203+
t1 = time.time()
204+
print((t1-t0)/N)
205+
206+
207+
#La_arr_vmap_compiled = perf(lambda: LaG_compiled.df_function(U=U_ri), "La compiled")
208+
# #print(La_arr_vmap)
209+
210+

0 commit comments

Comments
 (0)