| """Spatial-align attention mask for [latent | cond] concat self-attention. |
| |
| Ports OUR cross-attn xa_mask (model_multires.py L1326: latent patch (r,c) on the |
| GxG latent grid attends, per level s, the cond token covering it) into a single |
| (N+M)x(N+M) self-attn mask for Semanticist's DiT (which concats cond to the |
| latent sequence and runs full self-attn). |
| |
| Semantics preserved EXACTLY (2026-07-15 user requirement): |
| latent->latent : True (== our sa_mask=None, full) |
| latent->cond : our xa_mask spatial rule (same code as model_multires) |
| cond->latent : False (cond was never a query in cross-attn; conditioning |
| direction latent<-cond must be preserved) |
| cond->cond : identity only (attention must not mix cond tokens; every row |
| needs >=1 True or SDPA produces NaN) |
| Level-drop / uncond stays VALUE-based (learned null values), mask unchanged — |
| matches our learned_null_attend and Semanticist's null_cond. |
| """ |
| import torch |
|
|
|
|
| def build_xa_mask(G: int, level_sizes=(8, 4, 2, 1)) -> torch.Tensor: |
| """(N_img, M_cond) bool — EXACT port of model_multires.py L1326 loop. |
| G = latent grid side (DiT-L patch2 on 16x16 latent -> G=8). |
| level_sizes: multi-res cond grids, coarse order irrelevant (offsets follow list order). |
| """ |
| num_img = G * G |
| offsets, off = {}, 0 |
| for s in level_sizes: |
| offsets[s] = off |
| off += s * s |
| M = off |
| xa = torch.zeros(num_img, M, dtype=torch.bool) |
| for s in level_sizes: |
| start = offsets[s] |
| for r in range(G): |
| for c in range(G): |
| img_idx = r * G + c |
| if G >= s: |
| xa[img_idx, start + (r * s // G) * s + (c * s // G)] = True |
| else: |
| ratio = s // G |
| for dr in range(ratio): |
| for dc in range(ratio): |
| xa[img_idx, start + (r * ratio + dr) * s + (c * ratio + dc)] = True |
| return xa |
|
|
|
|
| def build_concat_self_attn_mask(G: int, level_sizes=(8, 4, 2, 1)) -> torch.Tensor: |
| """(N+M, N+M) bool self-attn mask, True=attend. Sequence = [latent N | cond M].""" |
| xa = build_xa_mask(G, level_sizes) |
| N, M = xa.shape |
| T = N + M |
| m = torch.zeros(T, T, dtype=torch.bool) |
| m[:N, :N] = True |
| m[:N, N:] = xa |
| |
| m[N:, N:] = torch.eye(M, dtype=torch.bool) |
| return m |
|
|
|
|
| if __name__ == "__main__": |
| |
| G, LS = 8, (8, 4, 2, 1) |
| xa = build_xa_mask(G, LS) |
| assert xa.shape == (64, 85), xa.shape |
| |
| assert (xa.sum(1) == len(LS)).all(), xa.sum(1) |
| |
| assert xa[0, 0] and xa[0, 64] and xa[0, 80] and xa[0, 84] |
| |
| assert xa[63, 63] and xa[63, 64 + 15] and xa[63, 80 + 3] and xa[63, 84] |
| |
| assert xa[28, 28] and xa[28, 64 + 6] and xa[28, 80 + 1] and xa[28, 84] |
| |
| assert not xa[0, 1] and not xa[0, 64 + 5] |
|
|
| m = build_concat_self_attn_mask(G, LS) |
| T = 64 + 85 |
| assert m.shape == (T, T) |
| assert m[:64, :64].all() |
| assert not m[64:, :64].any() |
| assert (m[64:, 64:] == torch.eye(85, dtype=torch.bool)).all() |
| assert (m.sum(1) >= 1).all() |
| |
| assert (m[:64, 64:] == xa).all() |
|
|
| |
| import torch.nn.functional as F |
| q = torch.randn(2, 4, T, 32); k = torch.randn(2, 4, T, 32); v = torch.randn(2, 4, T, 32) |
| out = F.scaled_dot_product_attention(q, k, v, attn_mask=m) |
| assert torch.isfinite(out).all() |
| print("ALL MASK TESTS PASSED ✅ (xa 64x85, concat 149x149, SDPA finite)") |
|
|