Kye Gomez commited on
Commit ·
53f786a
1
Parent(s): 14a806c
disclaimer and rope tests
Browse files- README.md +2 -0
- test_main.py +133 -0
README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
| 1 |
# OpenMythos
|
| 2 |
|
|
|
|
|
|
|
| 3 |
OpenMythos is an open-source, theoretical implementation of the Claude Mythos model. It implements a Recurrent-Depth Transformer (RDT) with three stages: **Prelude** (transformer blocks), a looped **Recurrent Block** (up to `max_loop_iters`), and a final **Coda**. Attention is switchable between MLA and GQA, and the feed-forward uses a sparse MoE with routed and shared experts ideal for exploring compute-adaptive, depth-variable reasoning.
|
| 4 |
|
| 5 |
|
|
|
|
| 1 |
# OpenMythos
|
| 2 |
|
| 3 |
+
> **Disclaimer:** OpenMythos is an independent, community-driven theoretical reconstruction based solely on publicly available research and speculation. It is not affiliated with, endorsed by, or connected to Anthropic or any of their proprietary systems.
|
| 4 |
+
|
| 5 |
OpenMythos is an open-source, theoretical implementation of the Claude Mythos model. It implements a Recurrent-Depth Transformer (RDT) with three stages: **Prelude** (transformer blocks), a looped **Recurrent Block** (up to `max_loop_iters`), and a final **Coda**. Attention is switchable between MLA and GQA, and the feed-forward uses a sparse MoE with routed and shared experts ideal for exploring compute-adaptive, depth-variable reasoning.
|
| 6 |
|
| 7 |
|
test_main.py
CHANGED
|
@@ -121,6 +121,139 @@ class TestRoPE:
|
|
| 121 |
assert not torch.allclose(out[0, 0], out[0, 1])
|
| 122 |
|
| 123 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
# ---------------------------------------------------------------------------
|
| 125 |
# GQAttention
|
| 126 |
# ---------------------------------------------------------------------------
|
|
|
|
| 121 |
assert not torch.allclose(out[0, 0], out[0, 1])
|
| 122 |
|
| 123 |
|
| 124 |
+
# ---------------------------------------------------------------------------
|
| 125 |
+
# RoPE extended — correctness invariants
|
| 126 |
+
# ---------------------------------------------------------------------------
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
class TestRoPEExtended:
|
| 130 |
+
"""Comprehensive correctness tests for precompute_rope_freqs and apply_rope."""
|
| 131 |
+
|
| 132 |
+
# --- precompute_rope_freqs ---
|
| 133 |
+
|
| 134 |
+
def test_position_zero_is_unit_phasor(self):
|
| 135 |
+
"""freqs[0] must be all 1+0j (angle = 0 * freq = 0 for every pair)."""
|
| 136 |
+
freqs = precompute_rope_freqs(dim=16, max_len=8)
|
| 137 |
+
expected = torch.ones(8, dtype=torch.complex64)
|
| 138 |
+
assert torch.allclose(freqs[0], expected, atol=1e-6)
|
| 139 |
+
|
| 140 |
+
def test_all_phasors_have_unit_magnitude(self):
|
| 141 |
+
"""Every phasor magnitude must be 1 — RoPE is an isometric rotation."""
|
| 142 |
+
freqs = precompute_rope_freqs(dim=16, max_len=32)
|
| 143 |
+
assert torch.allclose(freqs.abs(), torch.ones_like(freqs.abs()), atol=1e-6)
|
| 144 |
+
|
| 145 |
+
def test_angles_equal_outer_product(self):
|
| 146 |
+
"""freqs[t, k].angle() must equal t × base_freq[k] for all t, k."""
|
| 147 |
+
dim, max_len, theta = 8, 6, 500000.0
|
| 148 |
+
freqs = precompute_rope_freqs(dim=dim, max_len=max_len, theta=theta)
|
| 149 |
+
base = 1.0 / (theta ** (torch.arange(0, dim, 2, dtype=torch.float32) / dim))
|
| 150 |
+
t = torch.arange(max_len, dtype=torch.float32)
|
| 151 |
+
expected = torch.polar(torch.ones(max_len, dim // 2), torch.outer(t, base))
|
| 152 |
+
assert torch.allclose(freqs.real, expected.real, atol=1e-6)
|
| 153 |
+
assert torch.allclose(freqs.imag, expected.imag, atol=1e-6)
|
| 154 |
+
|
| 155 |
+
def test_higher_theta_produces_smaller_angles(self):
|
| 156 |
+
"""Larger theta → slower frequency decay → smaller rotation angle per step."""
|
| 157 |
+
dim, max_len = 16, 8
|
| 158 |
+
freqs_fast = precompute_rope_freqs(dim=dim, max_len=max_len, theta=100.0)
|
| 159 |
+
freqs_slow = precompute_rope_freqs(dim=dim, max_len=max_len, theta=500000.0)
|
| 160 |
+
assert (freqs_fast[1].angle().abs() > freqs_slow[1].angle().abs()).all()
|
| 161 |
+
|
| 162 |
+
def test_default_theta_matches_explicit(self):
|
| 163 |
+
"""Omitting theta must equal passing theta=500000.0."""
|
| 164 |
+
f1 = precompute_rope_freqs(16, 8)
|
| 165 |
+
f2 = precompute_rope_freqs(16, 8, theta=500000.0)
|
| 166 |
+
assert torch.allclose(f1.real, f2.real) and torch.allclose(f1.imag, f2.imag)
|
| 167 |
+
|
| 168 |
+
# --- apply_rope ---
|
| 169 |
+
|
| 170 |
+
def test_position_zero_is_identity(self):
|
| 171 |
+
"""T=1 input uses only freqs[0] = 1+0j, so output must equal input."""
|
| 172 |
+
freqs = precompute_rope_freqs(dim=16, max_len=8)
|
| 173 |
+
x = torch.randn(2, 1, 4, 16)
|
| 174 |
+
out = apply_rope(x, freqs)
|
| 175 |
+
assert torch.allclose(x, out, atol=1e-6)
|
| 176 |
+
|
| 177 |
+
def test_dtype_float32_preserved(self):
|
| 178 |
+
freqs = precompute_rope_freqs(dim=16, max_len=16)
|
| 179 |
+
x = torch.randn(1, 4, 2, 16).float()
|
| 180 |
+
assert apply_rope(x, freqs).dtype == torch.float32
|
| 181 |
+
|
| 182 |
+
def test_dtype_float16_preserved(self):
|
| 183 |
+
freqs = precompute_rope_freqs(dim=16, max_len=16)
|
| 184 |
+
x = torch.randn(1, 4, 2, 16).half()
|
| 185 |
+
assert apply_rope(x, freqs).dtype == torch.float16
|
| 186 |
+
|
| 187 |
+
def test_inverse_rotation_recovers_input(self):
|
| 188 |
+
"""Rotating by freqs then by conj(freqs) (inverse) must recover the original."""
|
| 189 |
+
dim = 16
|
| 190 |
+
freqs = precompute_rope_freqs(dim=dim, max_len=8)
|
| 191 |
+
x = torch.randn(2, 4, 3, dim)
|
| 192 |
+
rotated = apply_rope(x, freqs)
|
| 193 |
+
xc = torch.view_as_complex(rotated.float().reshape(*rotated.shape[:-1], -1, 2))
|
| 194 |
+
inv = freqs.conj()[: rotated.shape[1]].unsqueeze(0).unsqueeze(2)
|
| 195 |
+
recovered = torch.view_as_real(xc * inv).flatten(-2).to(x.dtype)
|
| 196 |
+
assert torch.allclose(x, recovered, atol=1e-5)
|
| 197 |
+
|
| 198 |
+
def test_batch_independence(self):
|
| 199 |
+
"""Output for one batch item must not depend on other items in the batch."""
|
| 200 |
+
dim = 16
|
| 201 |
+
freqs = precompute_rope_freqs(dim=dim, max_len=16)
|
| 202 |
+
torch.manual_seed(7)
|
| 203 |
+
x_a = torch.randn(1, 4, 2, dim)
|
| 204 |
+
x_b = torch.randn(1, 4, 2, dim)
|
| 205 |
+
solo = apply_rope(x_a, freqs)
|
| 206 |
+
batched = apply_rope(torch.cat([x_a, x_b], dim=0), freqs)[:1]
|
| 207 |
+
assert torch.allclose(solo, batched, atol=1e-6)
|
| 208 |
+
|
| 209 |
+
def test_head_independence(self):
|
| 210 |
+
"""All heads at the same position must receive identical rotations."""
|
| 211 |
+
dim = 16
|
| 212 |
+
freqs = precompute_rope_freqs(dim=dim, max_len=8)
|
| 213 |
+
x = torch.randn(1, 4, 1, dim).expand(1, 4, 3, dim).contiguous()
|
| 214 |
+
out = apply_rope(x, freqs)
|
| 215 |
+
assert torch.allclose(out[:, :, 0], out[:, :, 1], atol=1e-6)
|
| 216 |
+
assert torch.allclose(out[:, :, 1], out[:, :, 2], atol=1e-6)
|
| 217 |
+
|
| 218 |
+
def test_relative_position_property(self):
|
| 219 |
+
"""
|
| 220 |
+
Core RoPE invariant: <RoPE(q,m), RoPE(k,n)> depends only on (n-m).
|
| 221 |
+
Two pairs with the same offset must produce the same dot product.
|
| 222 |
+
"""
|
| 223 |
+
dim, max_len = 16, 32
|
| 224 |
+
freqs = precompute_rope_freqs(dim=dim, max_len=max_len)
|
| 225 |
+
torch.manual_seed(42)
|
| 226 |
+
q = torch.randn(1, 1, 1, dim)
|
| 227 |
+
k = torch.randn(1, 1, 1, dim)
|
| 228 |
+
|
| 229 |
+
def rope_at(tensor, pos):
|
| 230 |
+
"""Rotate tensor at a specific position by embedding it in a zero sequence."""
|
| 231 |
+
seq = torch.zeros(1, pos + 1, 1, dim)
|
| 232 |
+
seq[0, pos] = tensor[0, 0]
|
| 233 |
+
return apply_rope(seq, freqs)[:, pos : pos + 1]
|
| 234 |
+
|
| 235 |
+
# Both pairs have relative offset n - m = 6
|
| 236 |
+
dot_3_9 = (rope_at(q, 3) * rope_at(k, 9)).sum()
|
| 237 |
+
dot_1_7 = (rope_at(q, 1) * rope_at(k, 7)).sum()
|
| 238 |
+
assert torch.allclose(dot_3_9, dot_1_7, atol=1e-5)
|
| 239 |
+
|
| 240 |
+
def test_max_len_boundary(self):
|
| 241 |
+
"""apply_rope must handle T == max_len without error or NaN."""
|
| 242 |
+
max_len = 10
|
| 243 |
+
freqs = precompute_rope_freqs(dim=8, max_len=max_len)
|
| 244 |
+
x = torch.randn(1, max_len, 2, 8)
|
| 245 |
+
out = apply_rope(x, freqs)
|
| 246 |
+
assert out.shape == x.shape
|
| 247 |
+
assert not torch.isnan(out).any()
|
| 248 |
+
|
| 249 |
+
def test_exceeds_max_len_raises(self):
|
| 250 |
+
"""apply_rope must raise RuntimeError when T > max_len."""
|
| 251 |
+
freqs = precompute_rope_freqs(dim=8, max_len=4)
|
| 252 |
+
x = torch.randn(1, 8, 2, 8) # T=8 > max_len=4
|
| 253 |
+
with pytest.raises(RuntimeError):
|
| 254 |
+
apply_rope(x, freqs)
|
| 255 |
+
|
| 256 |
+
|
| 257 |
# ---------------------------------------------------------------------------
|
| 258 |
# GQAttention
|
| 259 |
# ---------------------------------------------------------------------------
|