Skip to content

Commit a2d8eda

Browse files
author
pytorchbot
committed
2026-07-11 nightly release (51c197c)
1 parent 0e39797 commit a2d8eda

6 files changed

Lines changed: 99 additions & 90 deletions

File tree

tests/integration_tests/models.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,30 @@ def _enable_spmd_backend(t: OverrideDefinitions, backend: str) -> OverrideDefini
1818
variant = tuple(
1919
arg.replace(f"{t.test_name}/", f"{test_name}/") for arg in variant
2020
)
21-
prefix = [f"--parallelism.spmd_backend {backend}"]
21+
is_qwen3_5 = any("--module qwen3_5" in arg for arg in variant)
22+
prefix = []
23+
if backend != "spmd_types" or not is_qwen3_5:
24+
prefix.append(f"--parallelism.spmd_backend {backend}")
2225
suffix = []
2326
# Compile, PP, and explicit AC modes are not compatible with SPMD
2427
# typechecking yet; keep those as backend-only coverage.
25-
if backend == "spmd_types" and not any(
26-
token in arg
27-
for arg in variant
28-
for token in (
29-
"compile.enable",
30-
"pipeline_parallel_degree",
31-
"activation-checkpoint:",
28+
if (
29+
prefix
30+
and backend == "spmd_types"
31+
and not any(
32+
token in arg
33+
for arg in variant
34+
for token in (
35+
"compile.enable",
36+
"pipeline_parallel_degree",
37+
"activation-checkpoint:",
38+
)
3239
)
3340
):
3441
prefix.append("--debug.spmd_typechecking")
3542
suffix.append("activation-checkpoint:none")
3643
new_args.append(tuple(prefix) + tuple(variant) + tuple(suffix))
44+
3745
return dataclasses.replace(
3846
t,
3947
override_args=tuple(new_args),

torchtitan/models/common/rope.py

Lines changed: 65 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,49 @@ def _maybe_check_max_pos(positions: torch.Tensor, *, max_valid_pos: int) -> None
3939
)
4040

4141

42+
def _yarn_inv_freq(
43+
dim: int,
44+
base: float,
45+
rope_factor: float,
46+
beta_fast: float,
47+
beta_slow: float,
48+
original_seq_len: int,
49+
truncate: bool,
50+
) -> torch.Tensor:
51+
"""Shared YaRN ("NTK-by-parts") inverse-frequency computation.
52+
53+
Single source of truth for both ``ComplexRoPE`` and ``CosSinRoPE`` so the
54+
two cache formats are guaranteed to agree. Follows the YaRN paper / HF
55+
convention: ``low <- beta_fast`` (extrapolation boundary), ``high <-
56+
beta_slow`` (interpolation boundary). ``truncate`` floors/ceils the cutoffs
57+
(DeepSeek style); ``truncate=False`` keeps fractional cutoffs (gpt-oss
58+
style). The range is always clamped to ``[0, dim/2 - 1]``. The YaRN
59+
attention "mscale" is intentionally NOT applied here -- the rope stays a
60+
pure rotation and the model folds mscale into its softmax scale.
61+
"""
62+
inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2)[: (dim // 2)].float() / dim))
63+
64+
def find_correction_dim(num_rotations: float) -> float:
65+
return (dim * math.log(original_seq_len / (num_rotations * 2 * math.pi))) / (
66+
2 * math.log(base)
67+
)
68+
69+
low = find_correction_dim(beta_fast)
70+
high = find_correction_dim(beta_slow)
71+
if truncate:
72+
low = math.floor(low)
73+
high = math.ceil(high)
74+
low, high = max(low, 0), min(high, dim - 1)
75+
assert (
76+
0 < low < high < dim - 1
77+
), f"Invalid YaRN params: 0 < {low} < {high} < {dim - 1}"
78+
79+
ramp = ((torch.arange(dim // 2, dtype=torch.float32) - low) / (high - low)).clamp(
80+
0, 1
81+
)
82+
return inv_freq / rope_factor * ramp + inv_freq * (1 - ramp)
83+
84+
4285
class RoPE(Module):
4386
"""Shared Rotary Position Embedding module.
4487
@@ -63,7 +106,7 @@ class Config(Module.Config):
63106
beta_fast: float = 32.0
64107
beta_slow: float = 1.0
65108
original_seq_len: int = 4096
66-
mscale: float = 0.0
109+
truncate: bool = True
67110

68111
def __init__(self, config: Config):
69112
super().__init__()
@@ -175,47 +218,15 @@ def _precompute_cache(self) -> torch.Tensor:
175218
freqs = torch.where(is_medium_freqs, smoothed_freqs, freqs)
176219
elif cfg.scaling == "yarn" and end > cfg.original_seq_len:
177220
# YaRN (DeepSeek V3 style)
178-
beta_fast = cfg.beta_fast
179-
beta_slow = cfg.beta_slow
180-
base = theta
181-
original_seq_len = cfg.original_seq_len
182-
factor = cfg.rope_factor
183-
184-
def find_correction_dim(
185-
num_rotations: float, dim: int, base: float, max_seq_len: int
186-
) -> float:
187-
return (
188-
dim
189-
* math.log(max_seq_len / (num_rotations * 2 * math.pi))
190-
/ (2 * math.log(base))
191-
)
192-
193-
def find_correction_range(
194-
low_rot: float,
195-
high_rot: float,
196-
dim: int,
197-
base: float,
198-
max_seq_len: int,
199-
) -> tuple[int, int]:
200-
low = math.floor(find_correction_dim(low_rot, dim, base, max_seq_len))
201-
high = math.ceil(find_correction_dim(high_rot, dim, base, max_seq_len))
202-
return max(low, 0), min(high, dim - 1)
203-
204-
def linear_ramp_factor(
205-
min_val: float, max_val: float, dim: int
206-
) -> torch.Tensor:
207-
if min_val == max_val:
208-
max_val += 0.001
209-
linear_func = (torch.arange(dim, dtype=torch.float32) - min_val) / (
210-
max_val - min_val
211-
)
212-
return torch.clamp(linear_func, 0, 1)
213-
214-
low, high = find_correction_range(
215-
beta_fast, beta_slow, dim, base, original_seq_len
221+
freqs = _yarn_inv_freq(
222+
dim,
223+
theta,
224+
cfg.rope_factor,
225+
cfg.beta_fast,
226+
cfg.beta_slow,
227+
cfg.original_seq_len,
228+
cfg.truncate,
216229
)
217-
smooth = 1 - linear_ramp_factor(low, high, dim // 2)
218-
freqs = freqs / factor * (1 - smooth) + freqs * smooth
219230

220231
t = torch.arange(end, device=freqs.device)
221232
freqs = torch.outer(t, freqs).float()
@@ -270,48 +281,30 @@ def _precompute_cache(self) -> torch.Tensor:
270281
max_seq_len = cfg.max_seq_len
271282
base = cfg.theta
272283

273-
freq = base ** (torch.arange(0, dim, 2)[: (dim // 2)].float() / dim)
274-
mscale = 1.0
275-
276284
if cfg.scaling == "llama":
277285
raise NotImplementedError("Cos/sin RoPE does not support Llama scaling.")
278286

279287
if cfg.scaling == "yarn" and cfg.rope_factor > 1.0:
280-
rope_factor = cfg.rope_factor
281-
# YaRN mscale for attention magnitude preservation
282-
mscale = 0.1 * math.log(rope_factor) + 1.0
283-
284-
# Compute correction range (NTK by parts)
285-
d_half = dim / 2
286-
low = (
287-
d_half
288-
* math.log(cfg.original_seq_len / (cfg.beta_slow * 2 * math.pi))
289-
/ math.log(base)
290-
)
291-
high = (
292-
d_half
293-
* math.log(cfg.original_seq_len / (cfg.beta_fast * 2 * math.pi))
294-
/ math.log(base)
288+
inv_freq = _yarn_inv_freq(
289+
dim,
290+
base,
291+
cfg.rope_factor,
292+
cfg.beta_fast,
293+
cfg.beta_slow,
294+
cfg.original_seq_len,
295+
cfg.truncate,
295296
)
296-
assert (
297-
0 < low < high < d_half - 1
298-
), f"Invalid YaRN params: 0 < {low} < {high} < {d_half - 1}"
299-
300-
ramp = (torch.arange(d_half, dtype=torch.float32) - low) / (high - low)
301-
mask = 1 - ramp.clamp(0, 1)
302-
303-
interpolation = 1.0 / (rope_factor * freq)
304-
extrapolation = 1.0 / freq
305-
inv_freq = interpolation * (1 - mask) + extrapolation * mask
306297
else:
307-
inv_freq = 1.0 / freq
298+
inv_freq = 1.0 / (
299+
base ** (torch.arange(0, dim, 2)[: (dim // 2)].float() / dim)
300+
)
308301

309302
t = torch.arange(max_seq_len, dtype=inv_freq.dtype, device=inv_freq.device)
310303
freqs = torch.outer(t, inv_freq).float()
311304
theta = torch.cat([freqs, freqs], dim=-1)
312305

313-
cos = theta.cos() * mscale
314-
sin = theta.sin() * mscale
306+
cos = theta.cos()
307+
sin = theta.sin()
315308
return torch.cat([cos, sin], dim=-1)
316309

317310
def _reshape_cache(

torchtitan/models/common/token_dispatcher.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ def _token_count_exchange(
269269
assert self.ep_mesh is not None
270270
if (
271271
torch.compiler.is_compiling() or torch.compiler._is_non_strict_tracing()
272-
) and get_spmd_backend() != "spmd_types":
272+
) or get_spmd_backend() != "spmd_types":
273273
return all_to_all_single(
274274
num_local_tokens_per_expert_E.view(ep_size, -1),
275275
None,
@@ -336,7 +336,7 @@ def _dispatch_token_exchange(
336336
assert self.ep_mesh is not None
337337
if (
338338
torch.compiler.is_compiling() or torch.compiler._is_non_strict_tracing()
339-
) and get_spmd_backend() != "spmd_types":
339+
) or get_spmd_backend() != "spmd_types":
340340
return all_to_all_single(
341341
routed_input_ND,
342342
output_splits,
@@ -364,7 +364,7 @@ def _combine_token_exchange(
364364
assert self.ep_mesh is not None
365365
if (
366366
torch.compiler.is_compiling() or torch.compiler._is_non_strict_tracing()
367-
) and get_spmd_backend() != "spmd_types":
367+
) or get_spmd_backend() != "spmd_types":
368368
return all_to_all_single(
369369
routed_output_RD,
370370
input_splits,

torchtitan/models/deepseek_v3/model.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ def forward(
132132
kv, [self.qk_nope_head_dim, self.v_head_dim], dim=-1
133133
)
134134
k = torch.cat([k_nope, k_pe.expand(-1, -1, k_nope.size(2), -1)], dim=-1)
135-
if get_spmd_backend() == "spmd_types":
135+
if get_spmd_backend() == "spmd_types" and not torch.compiler.is_compiling():
136136
for t in [k, v]:
137137
spmd.assert_type(
138138
t,

torchtitan/models/gpt_oss/__init__.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -283,8 +283,9 @@ def _debugmodel(
283283
theta=150000.0,
284284
scaling="yarn",
285285
rope_factor=32,
286-
beta_slow=32.0,
287-
beta_fast=1.0,
286+
beta_fast=32.0,
287+
beta_slow=1.0,
288+
truncate=False,
288289
original_seq_len=4096,
289290
),
290291
),
@@ -326,8 +327,9 @@ def _20b(
326327
theta=150000.0,
327328
scaling="yarn",
328329
rope_factor=32,
329-
beta_slow=32.0,
330-
beta_fast=1.0,
330+
beta_fast=32.0,
331+
beta_slow=1.0,
332+
truncate=False,
331333
original_seq_len=4096,
332334
),
333335
),
@@ -369,8 +371,9 @@ def _120b(
369371
theta=150000.0,
370372
scaling="yarn",
371373
rope_factor=32,
372-
beta_slow=32.0,
373-
beta_fast=1.0,
374+
beta_fast=32.0,
375+
beta_slow=1.0,
376+
truncate=False,
374377
original_seq_len=4096,
375378
),
376379
),

torchtitan/models/gpt_oss/model.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,11 @@ def __init__(self, config: Config):
104104

105105
# Standard attention softmax scale (1/sqrt(head_dim))
106106
self.softmax_scale = 1.0 / math.sqrt(self.head_dim)
107+
if config.rope.scaling == "yarn" and config.rope.rope_factor > 1.0:
108+
mscale = 0.1 * math.log(config.rope.rope_factor) + 1.0
109+
# Merge YaRN attention mscale into softmax_scale, with
110+
# m**2 being equivalent to scaling q / k each by mscale.
111+
self.softmax_scale *= mscale * mscale
107112

108113
self.qkv_linear = config.qkv_linear.build()
109114
self.wo = config.wo.build()

0 commit comments

Comments
 (0)