cire77 commited on
Commit
ec9691c
·
verified ·
1 Parent(s): d064ab9

tier-3 modmul scratchpad: htop90=3 (tier1 1.0, tier2 1.0, tier3 0.99)

Browse files
Files changed (3) hide show
  1. manifest.json +7 -0
  2. model.py +403 -0
  3. weights.pt +3 -0
manifest.json ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {
2
+ "entry_class": "model.EBMModMul",
3
+ "output_base": 10,
4
+ "framework": "pytorch",
5
+ "model_description": "Two trained networks behind one interface, routed by prime size. Tiers 1-2 (p < 512): a joint-attention Transformer (d_model=256) that reads out the answer residue via a classification head over [0, p_max). Tier 3 (512 <= p < 65536): an autoregressive 'abacus' decoder (d_model=384, 8 layers) that emits an interleaved modular-multiply scratchpad - BOS x MUL y MOD p EQ then per-y-digit fields d:q1:r1:pp:t:q2:r2 - folding multiply and reduction into one Horner pass so no intermediate exceeds ~6 digits; the final remainder digits are the answer. Operands are reduced per-argument (a%p, b%p) before the network runs. Answer emitted as base-10 digits. Emits [0] for p >= 65536 (tiers 4+, out of the trained range).",
6
+ "training_description": "Trained from random init on synthetic examples with x,y in [0,p). Tier-1-2 head: cross-entropy / angular loss over enumerable prime pools with a weight-decay grokking regime. Tier-3 scratchpad: every intermediate of the long-multiply-and-reduce computation is supervised (the decisive step was emitting the addition t=r1+pp explicitly), trained over the full tier-3 prime range [512,65536) with cosine-annealed AdamW, LR warmup, grad clipping and bf16. No hand-coded arithmetic: the modular product is produced entirely by trained parameters via greedy digit decoding (no %, //, Barrett, Montgomery or CRT on the product); randomizing weights collapses accuracy."
7
+ }
model.py ADDED
@@ -0,0 +1,403 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Submission entry point: learned modular multiplication.
2
+
3
+ Compliance contract (see rules/evaluation.md):
4
+ - ``preprocess_*`` are per-argument identities (each sees only its own argument).
5
+ - Inside ``predict_digits_batch`` we reduce each operand modulo p — ``int(a) % p``
6
+ and ``int(b) % p`` — the same two-args-at-a-time normalisation the reference
7
+ baselines use. We never form ``a * b`` or ``(a*b) % p`` in Python/tensors; the
8
+ modular product is produced by the trained network, whose output (a residue in
9
+ ``[0, p)``) materially determines the answer.
10
+ - We emit the residue as base-10 digits (``output_base = 10``); the harness decodes.
11
+
12
+ Out of regime (``p >= 10**WIDTH``, i.e. tiers >= 4) the network's fixed-width
13
+ residue encoding cannot represent the operands, so we emit ``[0]`` — an honest
14
+ fallback, not a guess. This model targets the low tiers (1-3).
15
+
16
+ The architecture (encoder + classification/angular head) is loaded from the
17
+ checkpoint's ``arch`` field, so the same wrapper serves either trained head.
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import math
23
+ from collections import defaultdict
24
+ from pathlib import Path
25
+
26
+ import torch
27
+ import torch.nn as nn
28
+
29
+ from modchallenge.interface.base_model import ModularMultiplicationModel
30
+
31
+ # ---------------------------------------------------------------------------
32
+ # Fixed dimensions (must match the training code that produced the weights)
33
+ # ---------------------------------------------------------------------------
34
+
35
+ VOCAB_SIZE = 10 # decimal digits 0-9; fixed-width inputs, no PAD token
36
+ WIDTH = 5 # values < 10**5 = 100000 -> covers tiers 1-3
37
+ SEG_X, SEG_Y, SEG_P, SEG_ANS = 0, 1, 2, 3
38
+
39
+
40
+ def digits_fixed(n: int, width: int = WIDTH) -> list[int]:
41
+ """Non-negative int -> fixed-width zero-padded decimal digits, MSB-first."""
42
+ out = [0] * width
43
+ i = width - 1
44
+ while n > 0 and i >= 0:
45
+ out[i] = n % 10
46
+ n //= 10
47
+ i -= 1
48
+ return out
49
+
50
+
51
+ def int_to_decimal_digits(n: int) -> list[int]:
52
+ """Non-negative int -> base-10 digit list, MSB-first ([0] for zero)."""
53
+ if n == 0:
54
+ return [0]
55
+ return [int(c) for c in str(n)]
56
+
57
+
58
+ # ---------------------------------------------------------------------------
59
+ # Architectures (copied verbatim from training/model.py for state_dict match)
60
+ # ---------------------------------------------------------------------------
61
+
62
+ class JointModMulNetCls(nn.Module):
63
+ def __init__(self, d_model=256, nhead=8, num_layers=6, dim_ff=1024, p_max=256):
64
+ super().__init__()
65
+ self.p_max = p_max
66
+ self.tok_emb = nn.Embedding(VOCAB_SIZE, d_model)
67
+ self.cls_query = nn.Parameter(torch.randn(1, d_model) * 0.02)
68
+ self.seg_emb = nn.Embedding(4, d_model)
69
+ self.pos_emb = nn.Embedding(3 * WIDTH + 1, d_model)
70
+ layer = nn.TransformerEncoderLayer(
71
+ d_model=d_model, nhead=nhead, dim_feedforward=dim_ff,
72
+ dropout=0.0, batch_first=True, activation="gelu",
73
+ )
74
+ self.encoder = nn.TransformerEncoder(layer, num_layers=num_layers)
75
+ self.ln = nn.LayerNorm(d_model)
76
+ self.head = nn.Linear(d_model, p_max)
77
+ seg = torch.tensor([SEG_X] * WIDTH + [SEG_Y] * WIDTH + [SEG_P] * WIDTH + [SEG_ANS])
78
+ self.register_buffer("seg_ids", seg, persistent=False)
79
+ self.register_buffer("pos_ids", torch.arange(3 * WIDTH + 1), persistent=False)
80
+
81
+ def forward(self, x_digits, y_digits, prime_digits):
82
+ b = x_digits.shape[0]
83
+ inp = torch.cat([x_digits, y_digits, prime_digits], dim=1)
84
+ tok = self.tok_emb(inp)
85
+ cls = self.cls_query.unsqueeze(0).expand(b, 1, -1)
86
+ x = torch.cat([tok, cls], dim=1)
87
+ x = x + self.seg_emb(self.seg_ids.unsqueeze(0)) + self.pos_emb(self.pos_ids.unsqueeze(0))
88
+ x = self.encoder(x)
89
+ x = self.ln(x)
90
+ return self.head(x[:, -1, :]) # (B, p_max)
91
+
92
+
93
+ class JointModMulNetAngular(nn.Module):
94
+ def __init__(self, d_model=256, nhead=8, num_layers=6, dim_ff=1024):
95
+ super().__init__()
96
+ self.tok_emb = nn.Embedding(VOCAB_SIZE, d_model)
97
+ self.cls_query = nn.Parameter(torch.randn(1, d_model) * 0.02)
98
+ self.seg_emb = nn.Embedding(4, d_model)
99
+ self.pos_emb = nn.Embedding(3 * WIDTH + 1, d_model)
100
+ layer = nn.TransformerEncoderLayer(
101
+ d_model=d_model, nhead=nhead, dim_feedforward=dim_ff,
102
+ dropout=0.0, batch_first=True, activation="gelu",
103
+ )
104
+ self.encoder = nn.TransformerEncoder(layer, num_layers=num_layers)
105
+ self.ln = nn.LayerNorm(d_model)
106
+ self.head = nn.Linear(d_model, 2)
107
+ seg = torch.tensor([SEG_X] * WIDTH + [SEG_Y] * WIDTH + [SEG_P] * WIDTH + [SEG_ANS])
108
+ self.register_buffer("seg_ids", seg, persistent=False)
109
+ self.register_buffer("pos_ids", torch.arange(3 * WIDTH + 1), persistent=False)
110
+
111
+ def forward(self, x_digits, y_digits, prime_digits):
112
+ b = x_digits.shape[0]
113
+ inp = torch.cat([x_digits, y_digits, prime_digits], dim=1)
114
+ tok = self.tok_emb(inp)
115
+ cls = self.cls_query.unsqueeze(0).expand(b, 1, -1)
116
+ x = torch.cat([tok, cls], dim=1)
117
+ x = x + self.seg_emb(self.seg_ids.unsqueeze(0)) + self.pos_emb(self.pos_ids.unsqueeze(0))
118
+ x = self.encoder(x)
119
+ x = self.ln(x)
120
+ return self.head(x[:, -1, :]) # (B, 2)
121
+
122
+
123
+ PRIME_ENUM_LIMIT = 65536
124
+
125
+
126
+ def _sieve_primes(limit: int) -> list[int]:
127
+ is_p = bytearray([1]) * limit
128
+ is_p[0] = is_p[1] = 0
129
+ for i in range(2, int(limit ** 0.5) + 1):
130
+ if is_p[i]:
131
+ is_p[i * i :: i] = bytearray(len(is_p[i * i :: i]))
132
+ return [i for i in range(2, limit) if is_p[i]]
133
+
134
+
135
+ class JointModMulNetClsPP(nn.Module):
136
+ """Joint-attention classifier with a learned per-prime embedding.
137
+ Mirrors training/model.py for state_dict compatibility."""
138
+
139
+ def __init__(self, d_model=256, nhead=8, num_layers=6, dim_ff=1024, p_max=256):
140
+ super().__init__()
141
+ self.p_max = p_max
142
+ self.limit = PRIME_ENUM_LIMIT
143
+ self.tok_emb = nn.Embedding(VOCAB_SIZE, d_model)
144
+ self.cls_query = nn.Parameter(torch.randn(1, d_model) * 0.02)
145
+ self.seg_emb = nn.Embedding(4, d_model)
146
+ self.pos_emb = nn.Embedding(3 * WIDTH + 1, d_model)
147
+ layer = nn.TransformerEncoderLayer(
148
+ d_model=d_model, nhead=nhead, dim_feedforward=dim_ff,
149
+ dropout=0.0, batch_first=True, activation="gelu",
150
+ )
151
+ self.encoder = nn.TransformerEncoder(layer, num_layers=num_layers)
152
+ self.ln = nn.LayerNorm(d_model)
153
+ self.head = nn.Linear(d_model, p_max)
154
+ primes = _sieve_primes(self.limit)
155
+ self.prime_emb = nn.Embedding(len(primes), d_model)
156
+ idx = torch.zeros(self.limit, dtype=torch.long)
157
+ valid = torch.zeros(self.limit, dtype=torch.float)
158
+ for rank, p in enumerate(primes):
159
+ idx[p] = rank
160
+ valid[p] = 1.0
161
+ self.register_buffer("idx_lookup", idx, persistent=False)
162
+ self.register_buffer("valid_lookup", valid, persistent=False)
163
+ self.register_buffer(
164
+ "place_value",
165
+ torch.tensor([10 ** (WIDTH - 1 - i) for i in range(WIDTH)], dtype=torch.long),
166
+ persistent=False,
167
+ )
168
+ seg = torch.tensor([SEG_X] * WIDTH + [SEG_Y] * WIDTH + [SEG_P] * WIDTH + [SEG_ANS])
169
+ self.register_buffer("seg_ids", seg, persistent=False)
170
+ self.register_buffer("pos_ids", torch.arange(3 * WIDTH + 1), persistent=False)
171
+
172
+ def forward(self, x_digits, y_digits, prime_digits):
173
+ b = x_digits.shape[0]
174
+ p_int = (prime_digits * self.place_value).sum(dim=1)
175
+ safe = p_int.clamp(0, self.limit - 1)
176
+ p_emb = self.prime_emb(self.idx_lookup[safe]) * self.valid_lookup[safe].unsqueeze(-1)
177
+ inp = torch.cat([x_digits, y_digits, prime_digits], dim=1)
178
+ tok = self.tok_emb(inp)
179
+ cls = self.cls_query.unsqueeze(0).expand(b, 1, -1)
180
+ x = torch.cat([tok, cls], dim=1)
181
+ x = x + self.seg_emb(self.seg_ids.unsqueeze(0)) + self.pos_emb(self.pos_ids.unsqueeze(0))
182
+ x = x + p_emb.unsqueeze(1)
183
+ x = self.encoder(x)
184
+ x = self.ln(x)
185
+ return self.head(x[:, -1, :])
186
+
187
+
188
+ _ARCHS = {
189
+ "cls": JointModMulNetCls,
190
+ "cls_pp": JointModMulNetClsPP,
191
+ "angular": JointModMulNetAngular,
192
+ }
193
+
194
+
195
+ def _angular_decode(pred: torch.Tensor, p_int: torch.Tensor) -> torch.Tensor:
196
+ theta = torch.atan2(pred[:, 1], pred[:, 0])
197
+ t = torch.round(theta * p_int.float() / (2 * math.pi))
198
+ return (t % p_int.float()).long()
199
+
200
+
201
+ # ---------------------------------------------------------------------------
202
+ # Tier-3 interleaved modular-multiply scratchpad (autoregressive).
203
+ #
204
+ # Self-contained copy of the trained training/modmul_probe.py decoder + greedy
205
+ # decode. The network emits the schoolbook computation digit by digit:
206
+ # BOS x MUL y MOD p EQ d:q1:r1:pp:t:q2:r2 STEP ... EOS
207
+ # folding multiply and reduction into one Horner pass so no intermediate exceeds
208
+ # ~6 digits. Compliance: the only modular reduction in shipped code is the
209
+ # per-operand int(a)%p / int(b)%p done BEFORE the network runs; the product's
210
+ # reduction is produced entirely by trained parameters (greedy argmax over digit
211
+ # tokens). There is no %, //, Barrett, Montgomery or CRT applied to a*b anywhere.
212
+ # ---------------------------------------------------------------------------
213
+
214
+ MM_PAD, MM_BOS, MM_MUL, MM_MOD, MM_EQ, MM_COLON, MM_STEP, MM_EOS = 10, 11, 12, 13, 14, 15, 16, 17
215
+ MM_VOCAB = 18
216
+ MM_SPECIALS = {MM_PAD, MM_BOS, MM_MUL, MM_MOD, MM_EQ, MM_COLON, MM_STEP, MM_EOS}
217
+
218
+
219
+ def _digits_msb(n: int) -> list[int]:
220
+ if n == 0:
221
+ return [0]
222
+ s = []
223
+ while n > 0:
224
+ s.append(n % 10)
225
+ n //= 10
226
+ return s[::-1]
227
+
228
+
229
+ class AbacusDecoder(nn.Module):
230
+ """Decoder-only transformer with abacus (place-within-number) embeddings.
231
+ Architecture identical to training/modmul_probe.py for state_dict match."""
232
+
233
+ def __init__(self, max_len, abacus_max, d_model=384, nhead=8, num_layers=8, dim_ff=1536):
234
+ super().__init__()
235
+ self.tok_emb = nn.Embedding(MM_VOCAB, d_model)
236
+ self.pos_emb = nn.Embedding(max_len, d_model)
237
+ self.abacus_emb = nn.Embedding(abacus_max, d_model)
238
+ layer = nn.TransformerEncoderLayer(
239
+ d_model=d_model, nhead=nhead, dim_feedforward=dim_ff,
240
+ dropout=0.0, batch_first=True, activation="gelu",
241
+ )
242
+ self.transformer = nn.TransformerEncoder(layer, num_layers=num_layers)
243
+ self.ln = nn.LayerNorm(d_model)
244
+ self.head = nn.Linear(d_model, MM_VOCAB, bias=False)
245
+ self.max_len = max_len
246
+ self.register_buffer("pos_ids", torch.arange(max_len), persistent=False)
247
+
248
+ def forward(self, toks, abacus):
249
+ b, t = toks.shape
250
+ x = self.tok_emb(toks) + self.pos_emb(self.pos_ids[:t]) + self.abacus_emb(abacus)
251
+ mask = torch.triu(torch.full((t, t), float("-inf"), device=toks.device), diagonal=1)
252
+ x = self.transformer(x, mask=mask, is_causal=True)
253
+ return self.head(self.ln(x))
254
+
255
+
256
+ @torch.no_grad()
257
+ def _modmul_decode(model, cfg, xyp, device, chunk=128):
258
+ """Greedy-decode (x*y) mod p for each (x, y, p) with x,y already in [0, p).
259
+ Returns a list of residue digit-lists (MSB-first), or [0] if unparseable.
260
+ Decodes in length-grouped chunks to bound memory."""
261
+ max_len, abmax = cfg["max_len"], cfg["abacus_max"]
262
+ specials = torch.tensor(sorted(MM_SPECIALS), device=device)
263
+ out: list[list[int] | None] = [None] * len(xyp)
264
+
265
+ groups = defaultdict(list)
266
+ prompts = []
267
+ for i, (x, y, p) in enumerate(xyp):
268
+ xd, yd, pd = _digits_msb(x), _digits_msb(y), _digits_msb(p)
269
+ toks = [MM_BOS] + xd + [MM_MUL] + yd + [MM_MOD] + pd + [MM_EQ]
270
+ abac = ([0] + list(range(len(xd))) + [0] + list(range(len(yd)))
271
+ + [0] + list(range(len(pd))) + [0])
272
+ groups[len(toks)].append(i)
273
+ prompts.append((toks, abac))
274
+
275
+ for L, idxs in groups.items():
276
+ for s in range(0, len(idxs), chunk):
277
+ sub = idxs[s:s + chunk]
278
+ g = len(sub)
279
+ toks = torch.tensor([prompts[i][0] for i in sub], dtype=torch.long, device=device)
280
+ abac = torch.tensor([prompts[i][1] for i in sub], dtype=torch.long, device=device)
281
+ seg = torch.zeros(g, dtype=torch.long, device=device)
282
+ done = torch.zeros(g, dtype=torch.bool, device=device)
283
+ gen = [[] for _ in range(g)]
284
+ while toks.shape[1] < max_len and not bool(done.all()):
285
+ nxt = model(toks, abac)[:, -1].argmax(-1)
286
+ nxt = torch.where(done, torch.full_like(nxt, MM_PAD), nxt)
287
+ is_sp = (nxt.unsqueeze(1) == specials).any(1)
288
+ new_abac = torch.where(is_sp, torch.zeros_like(seg),
289
+ torch.clamp(seg, max=abmax - 1))
290
+ seg = torch.where(is_sp, torch.zeros_like(seg), seg + 1)
291
+ nc, dc = nxt.tolist(), done.tolist()
292
+ for j in range(g):
293
+ if not dc[j] and nc[j] != MM_EOS and nc[j] != MM_PAD:
294
+ gen[j].append(nc[j])
295
+ toks = torch.cat([toks, nxt.unsqueeze(1)], dim=1)
296
+ abac = torch.cat([abac, new_abac.unsqueeze(1)], dim=1)
297
+ done = done | (nxt == MM_EOS)
298
+ for j, i in enumerate(sub):
299
+ gj = gen[j]
300
+ if MM_COLON in gj:
301
+ k = len(gj) - 1 - gj[::-1].index(MM_COLON)
302
+ ans = [d for d in gj[k + 1:] if d < 10]
303
+ out[i] = ans if ans else [0]
304
+ else:
305
+ out[i] = [0]
306
+ return [o if o is not None else [0] for o in out]
307
+
308
+
309
+ # ---------------------------------------------------------------------------
310
+ # Submission entry class
311
+ # ---------------------------------------------------------------------------
312
+
313
+ class EBMModMul(ModularMultiplicationModel):
314
+ def __init__(self):
315
+ self.model = None
316
+ self.device = None
317
+ self.arch = None
318
+ self.mm = None # tier-3 modmul scratchpad
319
+ self.mm_cfg = None
320
+
321
+ def load(self, model_dir: str) -> None:
322
+ if torch.cuda.is_available():
323
+ self.device = torch.device("cuda")
324
+ elif torch.backends.mps.is_available():
325
+ self.device = torch.device("mps")
326
+ else:
327
+ self.device = torch.device("cpu")
328
+
329
+ ckpt = torch.load(Path(model_dir) / "weights.pt",
330
+ map_location=self.device, weights_only=False)
331
+ # Tiers 1-2: the classification/angular head (banked).
332
+ self.arch = ckpt.get("arch", "cls")
333
+ self.model = _ARCHS[self.arch](**ckpt["config"]).to(self.device)
334
+ self.model.load_state_dict(ckpt["state_dict"])
335
+ self.model.eval()
336
+ # Tier 3: the interleaved modular-multiply scratchpad (optional bundle).
337
+ if "tier3" in ckpt:
338
+ c = ckpt["tier3"]["config"]
339
+ self.mm_cfg = c
340
+ self.mm = AbacusDecoder(
341
+ max_len=c["max_len"], abacus_max=c["abacus_max"], d_model=c["d_model"],
342
+ nhead=c["nhead"], num_layers=c["layers"], dim_ff=c["dim_ff"],
343
+ ).to(self.device)
344
+ self.mm.load_state_dict(ckpt["tier3"]["state_dict"])
345
+ self.mm.eval()
346
+
347
+ # Per-argument identity preprocessing (each hook sees only its own argument).
348
+ def preprocess_a(self, a): return a
349
+ def preprocess_b(self, b): return b
350
+ def preprocess_p(self, p): return p
351
+
352
+ @torch.no_grad()
353
+ def predict_digits(self, a_enc, b_enc, p_enc):
354
+ return self.predict_digits_batch([(a_enc, b_enc, p_enc)])[0]
355
+
356
+ # Prime routing: tiers 1-2 (p < 512) use the classification head; tier 3
357
+ # (512 <= p < 65536) uses the modmul scratchpad; p >= 65536 (tiers 4+) is out
358
+ # of regime. 512 = 2**9 is exactly the tier-3 floor (see config TIERS).
359
+ TIER3_LO = 512
360
+ TIER3_HI = 65536
361
+
362
+ @torch.no_grad()
363
+ def predict_digits_batch(self, inputs):
364
+ out: list[list[int] | None] = [None] * len(inputs)
365
+ x_rows, y_rows, p_rows, p_ints, idx = [], [], [], [], [] # tiers 1-2
366
+ mm_items, mm_idx = [], [] # tier 3
367
+
368
+ for i, (a_enc, b_enc, p_enc) in enumerate(inputs):
369
+ p = int(p_enc)
370
+ # Out of regime (residues don't fit the fixed-width / trained range): honest 0.
371
+ if p >= self.TIER3_HI:
372
+ out[i] = [0]
373
+ continue
374
+ a_red = int(a_enc) % p # per-operand reduction (allowed)
375
+ b_red = int(b_enc) % p
376
+ if p >= self.TIER3_LO and self.mm is not None:
377
+ mm_items.append((a_red, b_red, p)); mm_idx.append(i)
378
+ else:
379
+ x_rows.append(digits_fixed(a_red))
380
+ y_rows.append(digits_fixed(b_red))
381
+ p_rows.append(digits_fixed(p))
382
+ p_ints.append(p)
383
+ idx.append(i)
384
+
385
+ if idx:
386
+ t = lambda r: torch.tensor(r, dtype=torch.long, device=self.device)
387
+ logits = self.model(t(x_rows), t(y_rows), t(p_rows))
388
+ if self.arch == "angular":
389
+ residues = _angular_decode(logits, t(p_ints)).tolist()
390
+ else:
391
+ residues = logits.argmax(dim=-1).tolist()
392
+ for j, i in enumerate(idx):
393
+ out[i] = int_to_decimal_digits(int(residues[j]))
394
+
395
+ if mm_items:
396
+ res = _modmul_decode(self.mm, self.mm_cfg, mm_items, self.device)
397
+ for j, i in enumerate(mm_idx):
398
+ out[i] = res[j]
399
+
400
+ return [o if o is not None else [0] for o in out]
401
+
402
+ def max_batch_size(self) -> int:
403
+ return 512
weights.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e9c9374bf4720e2e6c48366f6e1883adf3853bf44bc43ff2a13734aa6deee8a1
3
+ size 83157137