File size: 6,876 Bytes
18cca89
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
"""
Standalone RoPE debug test β€” logs tensor outputs and intermediate calculations
so you can visually verify correctness of precompute_rope_freqs and apply_rope.
"""

import torch
from open_mythos.main import apply_rope, precompute_rope_freqs

DIM = 8
MAX_LEN = 6
THETA = 500000.0


def section(title: str) -> None:
    print(f"\n{'=' * 60}")
    print(f"  {title}")
    print("=" * 60)


def log(label: str, value) -> None:
    print(f"\n[{label}]")
    print(value)


# ---------------------------------------------------------------------------
# 1. precompute_rope_freqs β€” raw frequency table
# ---------------------------------------------------------------------------

section("1. precompute_rope_freqs")

freqs = precompute_rope_freqs(dim=DIM, max_len=MAX_LEN, theta=THETA)
log("freqs shape", freqs.shape)
log("freqs (complex)", freqs)
log("freqs.real", freqs.real)
log("freqs.imag", freqs.imag)
log("freqs magnitude (should be all 1.0)", freqs.abs())
log("freqs angle (radians)", freqs.angle())

print("\nExpected: base frequencies (1 per dim pair)")
base = 1.0 / (THETA ** (torch.arange(0, DIM, 2, dtype=torch.float32) / DIM))
log("base freqs", base)

print("\nExpected: freqs[t, k].angle() == t * base[k]")
for t in range(MAX_LEN):
    expected_angles = t * base
    actual_angles = freqs[t].angle()
    match = torch.allclose(actual_angles, expected_angles, atol=1e-5)
    print(f"  t={t}: angles match = {match}  actual={actual_angles.tolist()}")

# ---------------------------------------------------------------------------
# 2. Position 0 is identity (freqs[0] == 1+0j)
# ---------------------------------------------------------------------------

section("2. freqs[0] is identity phasor (1+0j)")
log("freqs[0]", freqs[0])
print(f"  All magnitude=1: {torch.allclose(freqs[0].abs(), torch.ones(DIM // 2))}")
print(f"  All angle=0:     {torch.allclose(freqs[0].angle(), torch.zeros(DIM // 2))}")

# ---------------------------------------------------------------------------
# 3. apply_rope β€” shape and dtype
# ---------------------------------------------------------------------------

section("3. apply_rope β€” shape and dtype")

B, T, H = 2, MAX_LEN, 3
x = torch.randn(B, T, H, DIM)
log("input x shape", x.shape)

out = apply_rope(x, freqs)
log("output shape", out.shape)
print(f"  Shape preserved: {out.shape == x.shape}")

# dtype float16
x_half = x.half()
out_half = apply_rope(x_half, freqs)
print(f"  float16 dtype preserved: {out_half.dtype == torch.float16}")

# ---------------------------------------------------------------------------
# 4. apply_rope β€” isometry (norm preservation)
# ---------------------------------------------------------------------------

section("4. apply_rope β€” norm preservation (isometry)")

norms_in = x.norm(dim=-1)
norms_out = out.norm(dim=-1)
log("input norms (first batch item)", norms_in[0])
log("output norms (first batch item)", norms_out[0])
print(
    f"  Max absolute norm difference: {(norms_in - norms_out).abs().max().item():.2e}"
)
print(
    f"  Norms preserved (atol=1e-5): {torch.allclose(norms_in, norms_out, atol=1e-5)}"
)

# ---------------------------------------------------------------------------
# 5. Position 0 is the identity transformation
# ---------------------------------------------------------------------------

section("5. Position 0 is identity transformation")

x1 = torch.randn(1, 1, 2, DIM)
out1 = apply_rope(x1, freqs[:1])
log("input  x[:,0]", x1[0, 0])
log("output x[:,0]", out1[0, 0])
log("diff (should be ~0)", (x1 - out1).abs())
print(f"  Identity at pos 0: {torch.allclose(x1, out1, atol=1e-6)}")

# ---------------------------------------------------------------------------
# 6. Different positions produce different rotations
# ---------------------------------------------------------------------------

section("6. Different positions produce different rotations")

x2 = torch.ones(1, MAX_LEN, 1, DIM)
out2 = apply_rope(x2, freqs)
print("Per-position outputs (all input=1.0):")
for t in range(MAX_LEN):
    print(f"  pos={t}: {out2[0, t, 0].tolist()}")

# ---------------------------------------------------------------------------
# 7. Inverse rotation recovers original
# ---------------------------------------------------------------------------

section("7. Inverse rotation recovers original")

x3 = torch.randn(2, T, H, DIM)
rotated = apply_rope(x3, freqs)
xc = torch.view_as_complex(rotated.float().reshape(*rotated.shape[:-1], -1, 2))
inv_freqs = freqs.conj()
recovered = (
    torch.view_as_real(xc * inv_freqs.unsqueeze(0).unsqueeze(2))
    .flatten(-2)
    .to(x3.dtype)
)
diff = (x3 - recovered).abs()
log("max recovery error", diff.max().item())
print(f"  Recovery succeeded (atol=1e-5): {torch.allclose(x3, recovered, atol=1e-5)}")

# ---------------------------------------------------------------------------
# 8. start_pos correctness β€” the core generation bug fix
# ---------------------------------------------------------------------------

section("8. start_pos correctness (generation bug)")

print(
    "Simulates: prompt of length 4, then decode step receives token at position 4.\n"
    "Old buggy code: freqs[:1] β†’ position 0 always.\n"
    "Fixed code: freqs[4:5] β†’ position 4."
)

prompt_len = 4
decode_token = torch.randn(1, 1, 1, DIM)

out_buggy = apply_rope(decode_token, freqs[:1])  # wrong: always pos 0
out_fixed = apply_rope(
    decode_token, freqs[prompt_len : prompt_len + 1]
)  # correct: pos 4

log("freqs[0] (pos 0)", freqs[0])
log(f"freqs[{prompt_len}] (pos {prompt_len})", freqs[prompt_len])
log("buggy output (pos 0 encoding)", out_buggy[0, 0, 0])
log("fixed output (pos 4 encoding)", out_fixed[0, 0, 0])
print(f"  Outputs differ (they should): {not torch.allclose(out_buggy, out_fixed)}")

# ---------------------------------------------------------------------------
# 9. Relative position property
# ---------------------------------------------------------------------------

section("9. Relative position property: <RoPE(q,m), RoPE(k,n)> depends only on (n-m)")

dim = 16
max_len = 32
freqs_big = precompute_rope_freqs(dim=dim, max_len=max_len, theta=THETA)
torch.manual_seed(42)
q = torch.randn(1, 1, 1, dim)
k = torch.randn(1, 1, 1, dim)


def rope_at(tensor, pos):
    seq = torch.zeros(1, pos + 1, 1, dim)
    seq[0, pos] = tensor[0, 0]
    return apply_rope(seq, freqs_big[: pos + 1])[:, pos : pos + 1]


dot_3_9 = (rope_at(q, 3) * rope_at(k, 9)).sum()
dot_1_7 = (rope_at(q, 1) * rope_at(k, 7)).sum()
dot_0_6 = (rope_at(q, 0) * rope_at(k, 6)).sum()
print(f"  <RoPE(q,3), RoPE(k,9)>: {dot_3_9.item():.6f}")
print(f"  <RoPE(q,1), RoPE(k,7)>: {dot_1_7.item():.6f}")
print(f"  <RoPE(q,0), RoPE(k,6)>: {dot_0_6.item():.6f}")
print(
    f"  All equal (offset=6): {torch.allclose(dot_3_9, dot_1_7, atol=1e-5) and torch.allclose(dot_1_7, dot_0_6, atol=1e-5)}"
)

section("DONE β€” all checks complete")