AbstractPhil commited on
Commit
df5e25f
·
verified ·
1 Parent(s): a635ed7

mirror attention.py from geolip 0.7.0 @a63a29b (MIRROR law; n_const=1 bit-identical)

Browse files
Files changed (1) hide show
  1. attention.py +127 -58
attention.py CHANGED
@@ -61,17 +61,51 @@ class CausalSDPA(nn.Module):
61
  return self.o(y.transpose(1, 2).reshape(B, 1, d))
62
 
63
 
64
- class CausalSplatHUB(nn.Module):
65
- def __init__(self, d: int, K: int = 512, D: int = 32, tau: float = 0.1,
66
- chunk: int = 256):
 
 
 
 
 
 
67
  super().__init__()
68
  self.addr = AlephAddress(K, D, tau)
69
- self.chunk = chunk # 256 measured best at ctx 2048 (bench)
70
  self.q = nn.Linear(d, D, bias=False)
71
  self.k = nn.Linear(d, D, bias=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  self.v = nn.Linear(d, d, bias=False)
73
  self.o = nn.Linear(d, d, bias=False)
74
- for m in (self.q, self.k, self.v, self.o):
75
  nn.init.orthogonal_(m.weight)
76
  self._mask_cache: dict = {}
77
  self._den_raw = None # (den tensor, floor) until read
@@ -102,34 +136,27 @@ class CausalSplatHUB(nn.Module):
102
  self._mask_cache[key] = m
103
  return m
104
 
 
 
 
 
 
 
 
105
  def _halves(self, x):
106
- qp, qn = self.addr.oriented(self.q(x))
107
- kp, kn = self.addr.oriented(self.k(x))
108
- return qp, qn, kp, kn, self.v(x)
 
 
 
 
109
 
110
- def forward(self, x):
111
- """Fast path: the two oriented halves run as ONE 2K-wide pass —
112
- every term is a sum of bilinear forms over the halves, so one
113
- pass over cat(p, n) is the same arithmetic in half the kernels
114
- (equal to forward_naive to fp reorder, ~1.5e-06; speed-harness
115
- verdict 2026-08-15: 1.7x eager, 4.0x under torch.compile)."""
116
- B, n, d = x.shape
117
- qc = self.addr.oriented_cat(self.q(x)) # (B, n, 2K)
118
- kc = self.addr.oriented_cat(self.k(x))
119
- v = self.v(x)
120
- C = min(self.chunk, n)
121
- pad = (-n) % C
122
- if pad:
123
- qc = F.pad(qc, (0, 0, 0, pad))
124
- kc = F.pad(kc, (0, 0, 0, pad))
125
- v = F.pad(v, (0, 0, 0, pad))
126
- nc = (n + pad) // C
127
  K2 = qc.shape[-1]
128
  qc = qc.view(B, nc, C, K2)
129
  kc = kc.view(B, nc, C, K2)
130
- v = v.view(B, nc, C, d)
131
- mask = self._mask(C, x.device, v.dtype)
132
-
133
  S = torch.einsum("bick,bicd->bikd", kc, v) # per-chunk 2KxD sums
134
  P = torch.cumsum(S, dim=1) - S # exclusive prefix
135
  zS = kc.sum(dim=2) # (B, nc, 2K)
@@ -138,9 +165,36 @@ class CausalSplatHUB(nn.Module):
138
  num = torch.einsum("bick,bikd->bicd", qc, P) + att @ v
139
  den = torch.einsum("bick,bik->bic", qc, zP).unsqueeze(-1) \
140
  + att.sum(dim=-1, keepdim=True)
 
141
 
142
- num = num.reshape(B, nc * C, d)[:, :n]
143
- den = den.reshape(B, nc * C, 1)[:, :n]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
  cl = dtype_floor(den)
145
  self._den_raw = (den.detach(), cl)
146
  self._den_stats = None
@@ -149,39 +203,54 @@ class CausalSplatHUB(nn.Module):
149
  # ---------------------------------------------------- incremental decode
150
  def prefill(self, x):
151
  """Full causal pass plus the decode cache. The hub's cache is the
152
- CONSTANT-SIZE prefix state (Sp, Sn, zp, zn) — O(K·d) regardless of
153
- sequence length; this is the linear-attention decode advantage."""
 
 
154
  out = self.forward(x)
155
- qp, qn, kp, kn, v = self._halves(x)
156
- cache = {"Sp": torch.einsum("bnk,bnd->bkd", kp, v),
157
- "Sn": torch.einsum("bnk,bnd->bkd", kn, v),
158
- "zp": kp.sum(dim=1), "zn": kn.sum(dim=1)}
159
- return out, cache
 
160
 
161
  def step(self, x_t, cache):
162
- """One new position: fold it into the prefix state, read once."""
163
- qp, qn, kp, kn, v = self._halves(x_t) # (B,1,K)/(B,1,d)
164
- kp1, kn1, v1 = kp.squeeze(1), kn.squeeze(1), v.squeeze(1)
165
- cache["Sp"] = cache["Sp"] + kp1.unsqueeze(-1) * v1.unsqueeze(1)
166
- cache["Sn"] = cache["Sn"] + kn1.unsqueeze(-1) * v1.unsqueeze(1)
167
- cache["zp"] = cache["zp"] + kp1
168
- cache["zn"] = cache["zn"] + kn1
169
- qp1, qn1 = qp.squeeze(1), qn.squeeze(1)
170
- num = torch.einsum("bk,bkd->bd", qp1, cache["Sp"]) \
171
- + torch.einsum("bk,bkd->bd", qn1, cache["Sn"])
172
- den = ((qp1 * cache["zp"]).sum(-1)
173
- + (qn1 * cache["zn"]).sum(-1)).unsqueeze(-1)
 
 
 
 
 
 
 
174
  return self.o((num / den.clamp_min(dtype_floor(den))).unsqueeze(1))
175
 
176
  def forward_naive(self, x):
177
  """Reference cumsum form (the validated probe-bed implementation).
178
- O(n·K·d) memory — test oracle only."""
179
- qp, qn, kp, kn, v = self._halves(x)
180
- Sp = torch.cumsum(torch.einsum("bnk,bnd->bnkd", kp, v), dim=1)
181
- Sn = torch.cumsum(torch.einsum("bnk,bnd->bnkd", kn, v), dim=1)
182
- zp = torch.cumsum(kp, dim=1)
183
- zn = torch.cumsum(kn, dim=1)
184
- num = torch.einsum("bnk,bnkd->bnd", qp, Sp) \
185
- + torch.einsum("bnk,bnkd->bnd", qn, Sn)
186
- den = (qp * zp).sum(-1, keepdim=True) + (qn * zn).sum(-1, keepdim=True)
 
 
 
 
 
187
  return self.o(num / den.clamp_min(dtype_floor(den)))
 
61
  return self.o(y.transpose(1, 2).reshape(B, 1, d))
62
 
63
 
64
+ class _Constellation(nn.Module):
65
+ """One codebook with its own routing-owned q/k frames (v2 form).
66
+
67
+ The multi-constellation hub is the PRODUCT-CODE form (B2: independent
68
+ frames compose, .859 -> .955 monotone in members) at lawful supply
69
+ (ROUND 5e: K <= 2*D per address space — v1's single 512-anchor book in
70
+ 32 dims ran 16x and crowded into 333-646 duplicate pairs)."""
71
+
72
+ def __init__(self, d: int, K: int, D: int, tau: float):
73
  super().__init__()
74
  self.addr = AlephAddress(K, D, tau)
 
75
  self.q = nn.Linear(d, D, bias=False)
76
  self.k = nn.Linear(d, D, bias=False)
77
+ nn.init.orthogonal_(self.q.weight)
78
+ nn.init.orthogonal_(self.k.weight)
79
+
80
+
81
+ class CausalSplatHUB(nn.Module):
82
+ def __init__(self, d: int, K: int = 512, D: int = 32, tau: float = 0.1,
83
+ chunk: int = 256, n_const: int = 1):
84
+ super().__init__()
85
+ if K > 2 * D:
86
+ import warnings
87
+ warnings.warn(
88
+ f"CausalSplatHUB supply K={K} exceeds 2*D={2*D}: anchors on "
89
+ f"a {D}-dim sphere past ~2x supply CROWD (measured — ROUND "
90
+ "5e shape ladder + the mini-beatrix-1 hub census: duplicate "
91
+ "pairs by the hundreds, consumed erank collapse). Provision "
92
+ "K <= 2*D or raise D.", stacklevel=2)
93
+ self.n_const = n_const
94
+ if n_const == 1:
95
+ # v1 layout, bit-for-bit: state-dict keys addr/q/k unchanged so
96
+ # every shipped checkpoint and the HF automodel mirror load.
97
+ self.addr = AlephAddress(K, D, tau)
98
+ self.q = nn.Linear(d, D, bias=False)
99
+ self.k = nn.Linear(d, D, bias=False)
100
+ nn.init.orthogonal_(self.q.weight)
101
+ nn.init.orthogonal_(self.k.weight)
102
+ else:
103
+ self.consts = nn.ModuleList(
104
+ _Constellation(d, K, D, tau) for _ in range(n_const))
105
+ self.chunk = chunk # 256 measured best at ctx 2048 (bench)
106
  self.v = nn.Linear(d, d, bias=False)
107
  self.o = nn.Linear(d, d, bias=False)
108
+ for m in (self.v, self.o):
109
  nn.init.orthogonal_(m.weight)
110
  self._mask_cache: dict = {}
111
  self._den_raw = None # (den tensor, floor) until read
 
136
  self._mask_cache[key] = m
137
  return m
138
 
139
+ # ------------------------------------------------ constellation access
140
+ def _units(self):
141
+ """Uniform view: [(addr, q, k)] whether single- or multi-book."""
142
+ if self.n_const == 1:
143
+ return [(self.addr, self.q, self.k)]
144
+ return [(c.addr, c.q, c.k) for c in self.consts]
145
+
146
  def _halves(self, x):
147
+ """Per-constellation oriented halves + shared values."""
148
+ outs = []
149
+ for addr, q, k in self._units():
150
+ qp, qn = addr.oriented(q(x))
151
+ kp, kn = addr.oriented(k(x))
152
+ outs.append((qp, qn, kp, kn))
153
+ return outs, self.v(x)
154
 
155
+ def _scan_cat(self, qc, kc, v, mask, B, n, nc, C, d):
156
+ """The exact chunked scan for one 2K-wide constellation."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  K2 = qc.shape[-1]
158
  qc = qc.view(B, nc, C, K2)
159
  kc = kc.view(B, nc, C, K2)
 
 
 
160
  S = torch.einsum("bick,bicd->bikd", kc, v) # per-chunk 2KxD sums
161
  P = torch.cumsum(S, dim=1) - S # exclusive prefix
162
  zS = kc.sum(dim=2) # (B, nc, 2K)
 
165
  num = torch.einsum("bick,bikd->bicd", qc, P) + att @ v
166
  den = torch.einsum("bick,bik->bic", qc, zP).unsqueeze(-1) \
167
  + att.sum(dim=-1, keepdim=True)
168
+ return num.reshape(B, nc * C, d)[:, :n], den.reshape(B, nc * C, 1)[:, :n]
169
 
170
+ def forward(self, x):
171
+ """Fast path: the two oriented halves run as ONE 2K-wide pass —
172
+ every term is a sum of bilinear forms over the halves, so one
173
+ pass over cat(p, n) is the same arithmetic in half the kernels
174
+ (equal to forward_naive to fp reorder, ~1.5e-06; speed-harness
175
+ verdict 2026-08-15: 1.7x eager, 4.0x under torch.compile).
176
+ Multi-constellation (n_const > 1): each book scans independently
177
+ and the reads compose BY BUDGET — numerators and agreement masses
178
+ sum across books before the single divide (never softmax over
179
+ books; B4 measured comparative composition at -.10)."""
180
+ B, n, d = x.shape
181
+ v = self.v(x)
182
+ C = min(self.chunk, n)
183
+ pad = (-n) % C
184
+ vp = F.pad(v, (0, 0, 0, pad)) if pad else v
185
+ nc = (n + pad) // C
186
+ vc = vp.view(B, nc, C, d)
187
+ mask = self._mask(C, x.device, v.dtype)
188
+ num = den = None
189
+ for addr, q, k in self._units():
190
+ qc = addr.oriented_cat(q(x)) # (B, n, 2K)
191
+ kc = addr.oriented_cat(k(x))
192
+ if pad:
193
+ qc = F.pad(qc, (0, 0, 0, pad))
194
+ kc = F.pad(kc, (0, 0, 0, pad))
195
+ nu, de = self._scan_cat(qc, kc, vc, mask, B, n, nc, C, d)
196
+ num = nu if num is None else num + nu
197
+ den = de if den is None else den + de
198
  cl = dtype_floor(den)
199
  self._den_raw = (den.detach(), cl)
200
  self._den_stats = None
 
203
  # ---------------------------------------------------- incremental decode
204
  def prefill(self, x):
205
  """Full causal pass plus the decode cache. The hub's cache is the
206
+ CONSTANT-SIZE prefix state (Sp, Sn, zp, zn) per constellation —
207
+ O(n_const·K·d) regardless of sequence length; this is the
208
+ linear-attention decode advantage. n_const == 1 keeps the exact
209
+ v1 cache shape (arms and the Space depend on it)."""
210
  out = self.forward(x)
211
+ halves, v = self._halves(x)
212
+ caches = [{"Sp": torch.einsum("bnk,bnd->bkd", kp, v),
213
+ "Sn": torch.einsum("bnk,bnd->bkd", kn, v),
214
+ "zp": kp.sum(dim=1), "zn": kn.sum(dim=1)}
215
+ for (qp, qn, kp, kn) in halves]
216
+ return out, (caches[0] if self.n_const == 1 else {"consts": caches})
217
 
218
  def step(self, x_t, cache):
219
+ """One new position: fold it into each prefix state, read once,
220
+ compose by budget across constellations."""
221
+ halves, v = self._halves(x_t) # (B,1,K)/(B,1,d)
222
+ caches = [cache] if self.n_const == 1 else cache["consts"]
223
+ v1 = v.squeeze(1)
224
+ num = den = None
225
+ for (qp, qn, kp, kn), c in zip(halves, caches):
226
+ kp1, kn1 = kp.squeeze(1), kn.squeeze(1)
227
+ c["Sp"] = c["Sp"] + kp1.unsqueeze(-1) * v1.unsqueeze(1)
228
+ c["Sn"] = c["Sn"] + kn1.unsqueeze(-1) * v1.unsqueeze(1)
229
+ c["zp"] = c["zp"] + kp1
230
+ c["zn"] = c["zn"] + kn1
231
+ qp1, qn1 = qp.squeeze(1), qn.squeeze(1)
232
+ nu = torch.einsum("bk,bkd->bd", qp1, c["Sp"]) \
233
+ + torch.einsum("bk,bkd->bd", qn1, c["Sn"])
234
+ de = ((qp1 * c["zp"]).sum(-1)
235
+ + (qn1 * c["zn"]).sum(-1)).unsqueeze(-1)
236
+ num = nu if num is None else num + nu
237
+ den = de if den is None else den + de
238
  return self.o((num / den.clamp_min(dtype_floor(den))).unsqueeze(1))
239
 
240
  def forward_naive(self, x):
241
  """Reference cumsum form (the validated probe-bed implementation).
242
+ O(n·K·d) memory — test oracle only. Sums constellations by budget,
243
+ matching forward()."""
244
+ halves, v = self._halves(x)
245
+ num = den = None
246
+ for qp, qn, kp, kn in halves:
247
+ Sp = torch.cumsum(torch.einsum("bnk,bnd->bnkd", kp, v), dim=1)
248
+ Sn = torch.cumsum(torch.einsum("bnk,bnd->bnkd", kn, v), dim=1)
249
+ zp = torch.cumsum(kp, dim=1)
250
+ zn = torch.cumsum(kn, dim=1)
251
+ nu = torch.einsum("bnk,bnkd->bnd", qp, Sp) \
252
+ + torch.einsum("bnk,bnkd->bnd", qn, Sn)
253
+ de = (qp * zp).sum(-1, keepdim=True) + (qn * zn).sum(-1, keepdim=True)
254
+ num = nu if num is None else num + nu
255
+ den = de if den is None else den + de
256
  return self.o(num / den.clamp_min(dtype_floor(den)))