import numpy as np import torch def segsum(x): T = x.size(-1) x_cumsum = torch.cumsum(x, dim=-1) x_segsum = x_cumsum[..., :, None] - x_cumsum[..., None, :] mask = torch.tril(torch.ones(T, T, device=x.device, dtype=bool)) x_segsum = x_segsum.masked_fill(~mask, -torch.inf) return x_segsum def construct_level_mask(level, L): T = L.size(-1) if level == 0: return torch.diag_embed(L[..., level, :]) indices = torch.cartesian_prod(torch.arange(T), torch.arange(T)).to(L.device) mask = torch.where( torch.logical_and( torch.logical_and( indices[:, 0] % (1 << level) >= (1 << (level - 1)), indices[:, 1] + (1 << (level - 1)) >= indices[:, 0] - (indices[:, 0] % (1 << (level - 1))), ), indices[:, 1] < indices[:, 0] - (indices[:, 0] % (1 << (level - 1))), ).view(T, T), L[..., level, :].unsqueeze(-1).expand(*([-1] * (len(L.shape) - 2)), T, T), 0, ) return mask def construct_H_matrix(a, L): T = a.size(-1) A = torch.exp(segsum(a)) H = torch.zeros_like(A) for level in range(int(np.ceil(np.log2(T))) + 1): mask = construct_level_mask(level, L) H += A * mask return H def naive_log_linear_attn(q, k, v, g, level_scales): H = construct_H_matrix(g.permute(0, 2, 1), level_scales.permute(0, 2, 3, 1)) M = torch.einsum("bhlc,blhn,bchn->bhlc", H, q, k) return torch.einsum("bhlc,bchp->blhp", M, v)