trojan0x commited on
Commit
a1e7f4a
Β·
verified Β·
1 Parent(s): 4e89fea

Add tests/test_ultron.py

Browse files
Files changed (1) hide show
  1. tests/test_ultron.py +279 -0
tests/test_ultron.py ADDED
@@ -0,0 +1,279 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Test suite for Ultron model β€” verifies forward pass, generation, stability, and all variants."""
2
+
3
+ import sys
4
+ import torch
5
+ import torch.nn.functional as F
6
+ import time
7
+
8
+ sys.path.insert(0, "/app")
9
+
10
+ from ultron.model import Ultron, UltronConfig
11
+ from ultron.variants import ultron_small, ultron_base, ultron_medium, ultron_medium_moe
12
+
13
+
14
+ def test_basic_forward():
15
+ """Test forward pass with minimal config."""
16
+ print("=" * 60)
17
+ print("TEST: Basic forward pass")
18
+ cfg = UltronConfig(
19
+ vocab_size=1000, dim=128, n_heads=4, n_kv_heads=2,
20
+ max_seq_len=128, prelude_layers=1, coda_layers=1,
21
+ recurrent_layers=2, max_loop_iters=4, lora_rank=4,
22
+ attn_type="gqa", use_moe=False,
23
+ )
24
+ model = Ultron(cfg)
25
+ total_params = model.get_num_params(non_embedding=False)
26
+ print(f" Config: dim={cfg.dim}, heads={cfg.n_heads}, recurrent_layers={cfg.recurrent_layers}, loops={cfg.max_loop_iters}")
27
+ print(f" Parameters: {total_params:,}")
28
+
29
+ ids = torch.randint(0, cfg.vocab_size, (2, 32))
30
+ logits = model(ids)
31
+ assert logits.shape == (2, 32, cfg.vocab_size), f"Wrong shape: {logits.shape}"
32
+ print(f" Logits shape: {logits.shape} βœ“")
33
+
34
+ # Check stability
35
+ rho = model.get_spectral_radius()
36
+ assert rho < 1.0, f"Spectral radius {rho} >= 1!"
37
+ print(f" Spectral radius ρ(A) = {rho:.6f} (< 1 βœ“)")
38
+ print(" PASSED βœ“\n")
39
+
40
+
41
+ def test_mla_attention():
42
+ """Test with Multi-Latent Attention."""
43
+ print("=" * 60)
44
+ print("TEST: MLA attention")
45
+ cfg = UltronConfig(
46
+ vocab_size=1000, dim=128, n_heads=4, n_kv_heads=4,
47
+ max_seq_len=128, prelude_layers=1, coda_layers=1,
48
+ recurrent_layers=2, max_loop_iters=4, lora_rank=4,
49
+ attn_type="mla",
50
+ kv_lora_rank=32, q_lora_rank=64,
51
+ qk_rope_head_dim=16, qk_nope_head_dim=16, v_head_dim=16,
52
+ )
53
+ model = Ultron(cfg)
54
+ ids = torch.randint(0, cfg.vocab_size, (2, 32))
55
+ logits = model(ids)
56
+ assert logits.shape == (2, 32, cfg.vocab_size)
57
+ print(f" Logits shape: {logits.shape} βœ“")
58
+ print(f" Parameters: {model.get_num_params():,}")
59
+ print(" PASSED βœ“\n")
60
+
61
+
62
+ def test_moe():
63
+ """Test with MoE FFN in recurrent block."""
64
+ print("=" * 60)
65
+ print("TEST: MoE FFN")
66
+ cfg = UltronConfig(
67
+ vocab_size=1000, dim=128, n_heads=4, n_kv_heads=2,
68
+ max_seq_len=128, prelude_layers=1, coda_layers=1,
69
+ recurrent_layers=2, max_loop_iters=4, lora_rank=4,
70
+ attn_type="gqa",
71
+ use_moe=True, n_experts=4, n_shared_experts=1,
72
+ n_experts_per_tok=2, expert_dim=64,
73
+ )
74
+ model = Ultron(cfg)
75
+ ids = torch.randint(0, cfg.vocab_size, (2, 16))
76
+ logits = model(ids)
77
+ assert logits.shape == (2, 16, cfg.vocab_size)
78
+ print(f" Logits shape: {logits.shape} βœ“")
79
+ print(f" Parameters: {model.get_num_params():,}")
80
+ print(" PASSED βœ“\n")
81
+
82
+
83
+ def test_generation():
84
+ """Test autoregressive generation with KV caching."""
85
+ print("=" * 60)
86
+ print("TEST: Autoregressive generation")
87
+ cfg = UltronConfig(
88
+ vocab_size=1000, dim=128, n_heads=4, n_kv_heads=2,
89
+ max_seq_len=256, prelude_layers=1, coda_layers=1,
90
+ recurrent_layers=2, max_loop_iters=4, lora_rank=4,
91
+ )
92
+ model = Ultron(cfg).eval()
93
+ prompt = torch.randint(0, cfg.vocab_size, (1, 8))
94
+ output = model.generate(prompt, max_new_tokens=16, n_loops=4, temperature=1.0, top_k=10)
95
+ assert output.shape == (1, 24), f"Expected (1, 24), got {output.shape}"
96
+ print(f" Generated shape: {output.shape} βœ“")
97
+ print(f" Prompt: {prompt[0].tolist()[:8]}")
98
+ print(f" Generated: {output[0, 8:].tolist()}")
99
+ print(" PASSED βœ“\n")
100
+
101
+
102
+ def test_depth_extrapolation():
103
+ """Test that model works with more loops at inference than training default."""
104
+ print("=" * 60)
105
+ print("TEST: Depth extrapolation")
106
+ cfg = UltronConfig(
107
+ vocab_size=1000, dim=128, n_heads=4, n_kv_heads=2,
108
+ max_seq_len=128, prelude_layers=1, coda_layers=1,
109
+ recurrent_layers=2, max_loop_iters=4, lora_rank=4,
110
+ )
111
+ model = Ultron(cfg).eval()
112
+ ids = torch.randint(0, cfg.vocab_size, (1, 16))
113
+
114
+ # Normal depth
115
+ logits_4 = model(ids, n_loops=4)
116
+ # Extrapolated depth (2x training loops)
117
+ logits_8 = model(ids, n_loops=8)
118
+ # Even deeper
119
+ logits_16 = model(ids, n_loops=16)
120
+
121
+ assert logits_4.shape == logits_8.shape == logits_16.shape
122
+ # Results should differ (different loop counts = different outputs)
123
+ assert not torch.allclose(logits_4, logits_8, atol=1e-4)
124
+ print(f" 4 loops β†’ logit mean: {logits_4.mean():.4f}")
125
+ print(f" 8 loops β†’ logit mean: {logits_8.mean():.4f}")
126
+ print(f" 16 loops β†’ logit mean: {logits_16.mean():.4f}")
127
+ print(" PASSED βœ“\n")
128
+
129
+
130
+ def test_act_halting():
131
+ """Verify ACT halting stops early when positions converge."""
132
+ print("=" * 60)
133
+ print("TEST: ACT halting behavior")
134
+ cfg = UltronConfig(
135
+ vocab_size=1000, dim=128, n_heads=4, n_kv_heads=2,
136
+ max_seq_len=128, prelude_layers=1, coda_layers=1,
137
+ recurrent_layers=2, max_loop_iters=16,
138
+ lora_rank=4, act_threshold=0.99,
139
+ )
140
+ model = Ultron(cfg).eval()
141
+ ids = torch.randint(0, cfg.vocab_size, (1, 16))
142
+ logits = model(ids, n_loops=16)
143
+ print(f" Logits shape: {logits.shape} βœ“")
144
+ print(f" ACT threshold: {cfg.act_threshold}")
145
+ print(" PASSED βœ“\n")
146
+
147
+
148
+ def test_backward():
149
+ """Test that gradients flow correctly through the looped model."""
150
+ print("=" * 60)
151
+ print("TEST: Backward pass / gradient flow")
152
+ cfg = UltronConfig(
153
+ vocab_size=1000, dim=128, n_heads=4, n_kv_heads=2,
154
+ max_seq_len=128, prelude_layers=1, coda_layers=1,
155
+ recurrent_layers=2, max_loop_iters=4, lora_rank=4,
156
+ )
157
+ model = Ultron(cfg)
158
+ optimizer = torch.optim.AdamW(model.parameters(), lr=1e-4)
159
+
160
+ ids = torch.randint(0, cfg.vocab_size, (2, 32))
161
+ labels = torch.randint(0, cfg.vocab_size, (2, 32))
162
+
163
+ logits = model(ids)
164
+ loss = torch.nn.functional.cross_entropy(
165
+ logits.view(-1, cfg.vocab_size), labels.view(-1)
166
+ )
167
+ loss.backward()
168
+ optimizer.step()
169
+
170
+ print(f" Loss: {loss.item():.4f}")
171
+
172
+ # Check key gradients exist
173
+ assert model.recurrent.injection.log_A.grad is not None, "No gradient on log_A!"
174
+ assert model.recurrent.injection.B.grad is not None, "No gradient on B!"
175
+ assert model.recurrent.injection.C.grad is not None, "No gradient on C!"
176
+ if model.recurrent.lora is not None:
177
+ assert model.recurrent.lora.B.grad is not None, "No gradient on LoRA B!"
178
+ print(f" log_A grad norm: {model.recurrent.injection.log_A.grad.norm():.6f}")
179
+ print(f" B grad norm: {model.recurrent.injection.B.grad.norm():.6f}")
180
+ print(f" C grad norm: {model.recurrent.injection.C.grad.norm():.6f}")
181
+ print(" PASSED βœ“\n")
182
+
183
+
184
+ def test_gradient_checkpointing():
185
+ """Test that gradient checkpointing works without errors."""
186
+ print("=" * 60)
187
+ print("TEST: Gradient checkpointing")
188
+ cfg = UltronConfig(
189
+ vocab_size=1000, dim=128, n_heads=4, n_kv_heads=2,
190
+ max_seq_len=128, prelude_layers=1, coda_layers=1,
191
+ recurrent_layers=2, max_loop_iters=4, lora_rank=4,
192
+ gradient_checkpointing=True,
193
+ )
194
+ model = Ultron(cfg)
195
+
196
+ ids = torch.randint(0, cfg.vocab_size, (2, 16))
197
+ labels = torch.randint(0, cfg.vocab_size, (2, 16))
198
+
199
+ logits = model(ids)
200
+ loss = torch.nn.functional.cross_entropy(logits.view(-1, cfg.vocab_size), labels.view(-1))
201
+ loss.backward()
202
+
203
+ print(f" Loss: {loss.item():.4f}")
204
+ print(f" Grad checkpointing enabled: {cfg.gradient_checkpointing}")
205
+ print(" PASSED βœ“\n")
206
+
207
+
208
+ def test_variant_param_counts():
209
+ """Verify parameter counts for all variants."""
210
+ print("=" * 60)
211
+ print("TEST: Variant parameter counts")
212
+
213
+ variants = {
214
+ "ultron_small": ultron_small(),
215
+ "ultron_base": ultron_base(),
216
+ }
217
+
218
+ for name, cfg in variants.items():
219
+ model = Ultron(cfg)
220
+ total = model.get_num_params(non_embedding=False)
221
+ non_emb = model.get_num_params(non_embedding=True)
222
+ rho = model.get_spectral_radius()
223
+ print(f" {name}: {total:>12,} total | {non_emb:>12,} non-emb | ρ(A)={rho:.6f}")
224
+ assert rho < 1.0
225
+
226
+ print(" PASSED βœ“\n")
227
+
228
+
229
+ def test_training_loop():
230
+ """Run a mini training loop to verify end-to-end training works."""
231
+ print("=" * 60)
232
+ print("TEST: Mini training loop (10 steps)")
233
+ cfg = UltronConfig(
234
+ vocab_size=1000, dim=128, n_heads=4, n_kv_heads=2,
235
+ max_seq_len=128, prelude_layers=1, coda_layers=1,
236
+ recurrent_layers=2, max_loop_iters=4, lora_rank=4,
237
+ )
238
+ model = Ultron(cfg)
239
+ optimizer = torch.optim.AdamW(model.parameters(), lr=3e-4)
240
+
241
+ losses = []
242
+ for step in range(10):
243
+ ids = torch.randint(0, cfg.vocab_size, (4, 64))
244
+ targets = ids[:, 1:]
245
+ logits = model(ids[:, :-1])
246
+ loss = F.cross_entropy(logits.reshape(-1, cfg.vocab_size), targets.reshape(-1))
247
+ loss.backward()
248
+ optimizer.step()
249
+ optimizer.zero_grad()
250
+ losses.append(loss.item())
251
+
252
+ rho = model.get_spectral_radius()
253
+ print(f" Step 0 loss: {losses[0]:.4f}")
254
+ print(f" Step 9 loss: {losses[-1]:.4f}")
255
+ print(f" ρ(A) after training: {rho:.6f} (< 1 βœ“)")
256
+ assert rho < 1.0, f"Spectral radius exploded: {rho}"
257
+ assert losses[-1] < losses[0], "Loss didn't decrease!"
258
+ print(" PASSED βœ“\n")
259
+
260
+
261
+ if __name__ == "__main__":
262
+ print("\nπŸ€– ULTRON TEST SUITE\n")
263
+ start = time.time()
264
+
265
+ test_basic_forward()
266
+ test_mla_attention()
267
+ test_moe()
268
+ test_generation()
269
+ test_depth_extrapolation()
270
+ test_act_halting()
271
+ test_backward()
272
+ test_gradient_checkpointing()
273
+ test_variant_param_counts()
274
+ test_training_loop()
275
+
276
+ elapsed = time.time() - start
277
+ print("=" * 60)
278
+ print(f"πŸŽ‰ ALL TESTS PASSED in {elapsed:.1f}s")
279
+ print("=" * 60)