rope_theta is ignored on the dense attention path (hardcoded 10000 / 1000000)
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 64/100
- Issue type
- Bug
- Clarity
- Mostly clear
- Activity status
- Active
- Domain
- machine-learning
Research direction
Start with the dense timescale construction in activations.h and CreateInvTimescale in ops/ops.h, then inspect ModelConfig serialization in configs.h and python/configs.cc. Check attention.cc to understand global-table selection. Done means the configured local and global theta values are serialized and used without changing existing outputs; verify against the existing goldens.
Written by the indexing model from the issue text.
Description
ModelConfig::rope_theta (configs.h:852) is read in exactly one place — the mla_* timescales in struct Activations (activations.h:512-520), i.e. the MLA path. The dense path builds its two tables from constants instead:
inv_timescale(
CreateInvTimescale(allocator, layer_config.qkv_dim,
layer_config.post_qk == PostQKType::HalfRope)),
inv_timescale_global(
CreateInvTimescale(allocator, max_qkv_dim,
layer_config.post_qk == PostQKType::HalfRope,
1000000.0, config.partial_rotary_factor)) {
(activations.h:126-132 — the 10000.0 for the local table comes from the default argument at ops/ops.h:30.)
Nothing is wrong today. Gemma 2 / PaliGemma / T5Gemma / Gemma3-270M want 10000 on every layer; Gemma 3 and Gemma 4 want 10000 local + 1e6 global; Qwen3 wants 1e6 and reaches it via use_global_timescale plus all-global windows. Every model in the tree matches one of the two constants, so this is latent rather than a live bug.
What concerns me is the failure mode for the next model. rope_theta looks like the knob for this, silently isn't on the dense path, and a mismatch produces no assert and no warning — just quietly degraded output that doesn't look like a position-encoding problem. A Llama-style 500000, or any future Gemma with different values, would run at 10000/1e6 and merely seem "worse than expected."
Proposed fix
- Pass
config.rope_thetato the localCreateInvTimescalecall (configis already a ctor parameter, so no new plumbing). - Add
ModelConfig::global_rope_theta = 1000000.0f, appended at the end ofVisitFieldsfor serialization compatibility, plus the matching entry inpython/configs.cc; pass it to the global call. - Optionally drop the
base_frequencydefault fromCreateInvTimescaleso every call site has to state its theta. - Set both fields explicitly in the Qwen3 configs, so their correctness is stated rather than incidental.
This is behavior-preserving: the only assignment of rope_theta anywhere is DeepSeek's = 10000.0f (configs.cc:672), which equals the default, and the new field's default equals the current literal — so every config in the tree produces bit-identical timescales. It should be checkable against existing goldens.
One design question
After this, use_global_timescale is arguably redundant: the selector at attention.cc:156 could use the global table whenever global_rope_theta != rope_theta. That removes a flag you can forget to set — setting only global_rope_theta today silently does nothing — but it touches the 10 configs that set it. Happy to keep the flag or drop it, whichever you prefer.
Glad to send a PR if this looks right.
(Separately, and I can file it on its own if it's worth a look: partial_rotary_factor is passed only to the global table, so on Gemma 4 2B — where 4 of every 5 layers are local — the local layers get full rotary. I couldn't tell from the code whether that asymmetry is intentional.)
- Dominant language
- C++
- Stars
- 7k
- Forks
- 660
- Avg merge
- 1d 3h
- Merged PRs (30d)
- 35
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from google/gemma.cpp
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
-
Difficulty 5/5 Over a week Newbie friendliness 35/100
-
Difficulty 4/5 3-5 days Newbie friendliness 68/100
-
Difficulty 5/5 Over a week Newbie friendliness 35/100
-
Difficulty 5/5 Over a week Newbie friendliness 35/100
All issues in google/gemma.cpp
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
duckdb/duckdb-wasm#2258 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
objectionary/eo-graphs#75 ·
-
Coarray integration tests carry no LABELS, so run_tests.py silently skips them under every backend Opencoarray
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
FISCO-BCOS/FISCO-BCOS#5642 ·