Skip to content

Experiment #2 by semantic owner: the rotary schedule, and two declarations nothing reads - #375

Merged
chrishayuk merged 5 commits into
mainfrom
rope-layer-schedule
Sep 1, 2026
Merged

Experiment #2 by semantic owner: the rotary schedule, and two declarations nothing reads#375
chrishayuk merged 5 commits into
mainfrom
rope-layer-schedule

Conversation

@chrishayuk

@chrishayuk chrishayuk commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Fixes 2, 3 and 4 of census experiment #2, by semantic owner. Three commits, three different treatments — and in every case the recorded classification was wrong.


1. no_rope_layers — the key means the opposite of its name

SmolLM3 and Llama 4 both declare which layers encode position with no_rope_layers plus a no_rope_layer_interval fallback. SmolLM3's config documents it as:

A 1 at an index position indicates that the corresponding layer will use RoPE, while a 0 indicates that it's a NoPE layer.

and both references read it as self.use_rope = config.no_rope_layers[layer_idx].

A reader who trusts the name inverts the entire schedule. On SmolLM3-3B that is 27 of 36 layers rotated that must not be, and 9 left unrotated that must be — and the model still emits fluent text, because NoPE layers are a minority and attention degrades gracefully. Nothing but a test pinned against the reference catches it.

Honoured exactly once, in PositionPolicy::rope_enabled_by_flag, beside the layer_rope_theta zero sentinel it is the sibling of.

Not an alias, as recorded: ALIAS_KEYS requires the canonical spelling present in the same config, and SmolLM3 declares no layer_rope_theta at all. The capability was shared; the spelling had no reader.

Placed in the trait default, by the criterion rotary_policy's doc already states — two unrelated families declare it with identical semantics, so it is a config fact rather than a family fact. The schedule is asked before the rotary shape and composes with it, so a scheduled layer still picks up YaRN, a partial rotary, or a per-layer theta.

no_rope_layer_interval is a fallback generator, not a restatement: both references consult it only if no_rope_layers is None. Carried at Parsed, because on a checkpoint declaring both it contributes nothing to the graph.

Two malformed cases decline to answer, both in the direction that preserves existing behaviour: a short mask says nothing about the layers past its end, and an interval of zero is not a schedule. Clippy's manual_is_multiple_of is what surfaced that my first zero-guard fell the wrong way — it would have turned a malformed declaration into a silently NoPE model.

Calibrated: inverting the flag fails 5 of 7 gates including the real 36-layer schedule. The two survivors declare no schedule at all — the control.


2. rope_interleaved and use_mrope — vestigial, and read anyway

A gap kind distinct from the declared-disabled one. use_mrope appears nowhere in transformers 5.5.0; rope_interleaved (exact spelling) appears nowhere either — SmolLM2-135M declares it under model_type: llama, which has no such field. Unlike use_sliding_window, which HF genuinely consults, there is no upstream behaviour to agree with.

That makes reading them more important, not less. Neither names a cosmetic fact:

rope_interleaved   which dimensions rotate against which partner
use_mrope          whether position is one axis or three

Each is checked against the effective policy, never against itself:

  • rope_interleaved against ROPE_PAIRING_INTERLEAVED, a new constant naming this build's one pairing — split-half, (x[i], x[i + half]).
  • use_mrope against whether mrope_section + mrope_interleaved actually resolved an MRope policy. A probe echoing the flag back would agree with a checkpoint claiming multi-axis while carrying no geometry to build one from.

the_executor_pairs_split_half keeps the constant honest in both directions: the expected partner is derived from the constant, so flipping it flips what the test demands and it then fails against an unchanged executor. Calibrated by making the executor interleaved for real — the gate goes red.

The parser reads these and stops. Reading a flag must not become acting on it: this build has one pairing and the planner reports the disagreement, where a parser that quietly switched operators would turn a reportable mismatch into the silent wrong answer the report exists to prevent.


3. Probe coverage

Both new carriage probes from the previous PR were declared and unexercised, which took carriage.rs under the 90% floor — and left the claim that these rules are checkable resting on nothing. Each now gets paired arms. The null arm is the one worth having: a stack that rotates nowhere answers null for position_embedding_type, and a checkpoint declaring rope over it must stop reading as satisfied — the granitemoehybrid case, reproduced on a fixture.


Census

HuggingFaceTB/SmolLM3-3B      3 → 1 blocking
HuggingFaceTB/SmolLM2-135M    3 → 2 blocking
Qwen/Qwen2.5-0.5B             2 → 1 blocking
everything else               unchanged

All three residuals are pre-existing and outside this cluster: pretraining_tp and is_llama_config are training/identity facts, and Qwen2.5 keeps the honest sliding_window mismatch — declared 32768, carried null, because the graph records the effect and not the reason.

Gates

fmt --all --check · check --workspace --all-targets (four new ModelConfig fields) · clippy with each touched crate's own CI flags, including larql-compute with and without --all-features · full suites for larql-models, larql-compute, larql-vindex, larql-server · coverage policy for all three gated crates, re-measured after llvm-cov clean.

…he opposite of its name

SmolLM3 and Llama 4 both declare which layers encode position with

    no_rope_layers        [1,1,1,0, 1,1,1,0, ...]
    no_rope_layer_interval  4

and the key is named for what it disables while its values say the
opposite. SmolLM3's config documents it as *"A `1` at an index position
indicates that the corresponding layer will use RoPE, while a `0`
indicates that it's a NoPE layer"*, and both references read it as
`self.use_rope = config.no_rope_layers[layer_idx]`.

So a reader who trusts the name inverts the whole schedule. On SmolLM3-3B
that is 27 of 36 layers rotated that must not be and 9 left unrotated that
must be — and the model still emits fluent text, because NoPE layers are
a minority and attention degrades gracefully rather than failing. The
polarity is honoured exactly once, in `PositionPolicy::rope_enabled_by_flag`,
beside the `layer_rope_theta` zero sentinel it is the sibling of.

The census recorded this as an "alias spelling" of the existing per-layer
NoPE capability. It is not: `ALIAS_KEYS` requires the canonical spelling
present in the SAME config and read there, and SmolLM3 declares no
`layer_rope_theta` at all. The capability is shared; the spelling had no
reader.

**Placed in the trait default, not a family override** — the opposite of
the `position_embedding_type` fix in the previous commit, and by the
criterion `rotary_policy` already states: two unrelated families
(`smollm3`, `llama4`) declare this key with identical semantics, so it is
a config fact rather than a family fact. SmolLM3 resolves to `GenericArch`
and needs no impl of its own.

The schedule is asked BEFORE the rotary shape and composes with it: a
scheduled layer still picks up YaRN, a partial rotary, or a per-layer
theta. Answering the schedule by returning a bare `Rope { theta: default }`
would have silently re-based every rotating layer.

`no_rope_layer_interval` is a FALLBACK GENERATOR, not a redundant
spelling: both references consult it only `if no_rope_layers is None`, so
an explicit mask supersedes it rather than being reconciled with it.
Carried at `Parsed` for that reason — on a checkpoint declaring both it
contributes nothing to the graph, the same way `max_window_layers` is
inert while the window is disabled.

Two malformed cases decline to answer rather than guessing, both in the
direction that preserves existing behaviour: a mask shorter than the stack
says nothing about the layers past its end, and an interval of zero is not
a schedule (upstream divides by it and raises), so it leaves layers
unscheduled instead of turning a bad declaration into a silently NoPE
model.

Calibrated: inverting `rope_enabled_by_flag` fails 5 of the 7 gates,
including the real 36-layer SmolLM3 schedule. The two that survive are
the ones that declare no schedule at all, which is correct.

Census, from cached headers:

    HuggingFaceTB/SmolLM3-3B    3 -> 1 blocking   (residual: pretraining_tp)
    everything else             unchanged
`probe_no_rope_layers` and `probe_position_embedding_type` were
declared and unexercised, which took carriage.rs to 89.04% against the
90% per-file floor — and, more to the point, left the claim that these
rules are CHECKABLE resting on nothing.

Each gets the paired arms this file requires: a declaration the graph
carries, and a second declaration that changes the carried answer. The
second is what makes the first non-vacuous — a probe that echoed its
input would pass the positive arm alone.

The null arm is the one worth having. A stack whose schedule rotates
nowhere answers `null` for `position_embedding_type`, and a checkpoint
declaring `rope` over it must stop reading as satisfied. That is exactly
the granitemoehybrid case the rule was added for, reproduced on a fixture.
…ed and use_mrope

Both are VESTIGIAL, and that is a distinct gap kind from the
declared-disabled one `use_sliding_window` established. `use_mrope`
appears nowhere in transformers 5.5.0; `rope_interleaved` (exact
spelling) appears nowhere either — SmolLM2-135M declares it under
`model_type: llama`, which has no such field. So unlike
`use_sliding_window`, which HF genuinely consults, there is no upstream
behaviour to agree with. Only this build's.

That makes reading them MORE important, not less. Neither names a
cosmetic fact:

    rope_interleaved   which dimensions rotate against which partner
    use_mrope          whether position is one axis or three

and an unread declaration that happens to agree is one value away from a
silent wrong answer. An interleaved rotation produces plausible numbers
of the same magnitude against different partners; nothing downstream
looks wrong.

Each is checked against the EFFECTIVE policy, never against itself:

- `rope_interleaved` against `ROPE_PAIRING_INTERLEAVED`, a new constant
  naming this build's one pairing — split-half, `(x[i], x[i + half])`.
- `use_mrope` against whether `mrope_section` + `mrope_interleaved`
  actually resolved a `PositionPolicy::MRope`. A probe echoing the flag
  back would agree with a checkpoint claiming multi-axis while carrying
  no axis geometry to build one from.

`the_executor_pairs_split_half` keeps the constant honest, and does it in
BOTH directions: the expected partner is derived from the constant, so
flipping the constant flips what the test demands and it then fails
against an unchanged executor. Calibrated by making the executor
interleaved for real — the gate goes red, and the constant alone would
not have noticed.

The parser reads these and stops there. Reading a flag must not become
acting on it: this build has one pairing and the planner reports the
disagreement, where a parser that quietly switched operators would turn
a reportable mismatch into exactly the wrong answer the report exists to
prevent.

Census, from cached headers:

    HuggingFaceTB/SmolLM2-135M    3 -> 2 blocking
    Qwen/Qwen2.5-0.5B             2 -> 1 blocking

Both residuals are pre-existing and outside this cluster: SmolLM2 keeps
`is_llama_config` and `pretraining_tp`; Qwen2.5 keeps the honest
`sliding_window` mismatch C recorded — declared 32768, carried null,
because the graph records the effect and not the reason.
@chrishayuk chrishayuk changed the title The per-layer rotary schedule — no_rope_layers means the opposite of its name Experiment #2 by semantic owner: the rotary schedule, and two declarations nothing reads Sep 1, 2026
…e census stops counting mysteries

GLM-5.3-Flash was the census's designated falsifier: the cluster where
"this is genuinely new execution work" had to be sayable, because a
taxonomy that scored a learned sparse-attention indexer as another
normalisation gap would be broken rather than impressive.

It could not say it. Every one of those keys graded `unknown`, which
means "nobody has looked" — right as a finding, useless as an estimate.
A report made mostly of `unknown` tells you how much was unexamined, not
how much is left to build.

`SemanticClass::UnsupportedComponent` says the other thing: somebody
looked, and there is a model component behind the key that this build
does not implement. Blocking exactly as before — the class changes what
the report SAYS, never how much it permits, and
`a_key_configuring_an_absent_component_grades_unsupported_and_blocks`
asserts both halves. GLM still blocks 32.

    before   24 unknown
    after    12 keys -> 2 named components, 13 unknown

**Entries are earned by evidence, not adjacency.** The indexer is grouped
on a geometry separate from the model's own (`index_n_heads: 32`,
`index_head_dim: 128`, against the text stack's `qk_head_dim: 256`), a
selection count rather than a width (`index_topk: 2048`), one coherent
pooling mechanism, and an `indexer_types` array of exactly 45 entries
against `num_hidden_layers: 45` — a per-layer schedule for this component
the way `layer_types` is for attention. Hyper-connections are grouped on
the `hc_` prefix plus `hc_sinkhorn_iters`, which names an algorithm
rather than a generic knob.

`mhc: true` sits directly beside the `hc_*` trio and is very likely part
of the same component. It is NOT listed, and
`a_neighbouring_key_with_no_evidence_stays_unknown` pins that. A bare
boolean whose expansion cannot be checked is exactly what this table must
not swallow, or it becomes the convenient bucket that makes every
estimate look tidier than the evidence supports.

**Nothing here is implemented, deliberately.** There is no reference to
implement against: `glm5_next` is absent from transformers 5.5.0 and the
repo ships no remote modeling code (72 files, zero `.py`) — checked, not
assumed. Every other fix in this cluster leaned on reading the upstream
loader, and that instrument does not exist for this family. So the
component is named and refused, which is the difference between an
engineering estimate and a compatibility claim.

Closes the miscluster the experiment started from:
`indexer_rope_interleave` is the INDEXER's rotary pairing, swept into the
general RoPE cluster by a regex over `rope`. Acting on it there would
have applied an interleaved pairing to the whole model — wrong component
and wrong operator — and the checkpoint declares `true`, so the mistake
would have been live rather than latent.
Review wording, adopted verbatim: the class means a model component has
been POSITIVELY IDENTIFIED from the checkpoint semantics and this build
has no implementation for it. Stated against its neighbours, because the
engineering implication is what differs — not a parser chore, not a
spelling alias, not a checked default, not an inactive declaration.
There is machinery missing.

The registration rule is now written where someone adding an entry will
read it, rather than living in a review thread:

    positive evidence of component ownership, never plausible adjacency

with the shortcuts that would make the table cheap to extend ruled out by
name — no prefix-only registration, no regex rule ever, and an adjacent
key stays `unknown`. A pattern is how these keys were FOUND; it may not
be the authority. That distinction is not abstract: a regex over `rope`
is what filed `indexer_rope_interleave` under general RoPE, where acting
on it would have re-paired the whole model's rotary.

The cost of relaxing the rule is not a wrong label. It is an engineering
estimate that reads tidier than the evidence supports, which is worse
than no estimate at all.
@chrishayuk
chrishayuk merged commit e9604dc into main Sep 1, 2026
42 of 46 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant