The Function Does Not Travel Either
Ongoing research. This is an experimental result from active work, not a settled conclusion. The numbers are what we measured and the method is described so you can judge it, but the programme is still running and later experiments may revise what it means.
A note on the language in these records. This is a working laboratory notebook for research into training AI models more cheaply and efficiently, so you will read that an approach did not work, that a result did not hold up, or that one method was worse than another. That is the research doing its job, not a verdict on the engineering we deliver to clients. Ruling an approach out is how the search narrows, and these are the pages that teach us the most: nearly every technique we now rely on came from understanding why something else fell short. Testing our own ideas at least as hard as anyone else's is the point of publishing them. More about this programme and why we run it.
Part of a bigger question: Which parts of a model actually matter? – The parts that move most are not the parts that matter, and no single component is required -- the model routes around every freeze. What a part is worth shows up only when you remove it.
In plain English
What we asked. A few weeks ago we took the most important direction of change inside one small model, just before it learned, and pushed a second model along it. It helped no more than a random push. A reviewer suggested why: the direction is written in one model's private coordinates, but what it does to the model's answers might be shared. So we copied the effect on the answers instead, rebuilt inside the second model.
What we found. It still helped no more than a random push. The second model could reproduce almost all of the first model's change in answers, and it did not learn any sooner for it. The only nudges that helped were ones taken from the model's own training.
Why it matters. We also got this experiment wrong three times before, each time by rewriting the original code and changing a detail by accident. The fourth time we reused the original code exactly and checked that it reproduced every original number first. That habit is the part worth copying.
The rest of this page is the technical record: the design, every number, and the limits. It is written for a reviewer, and you do not need it to have understood the result above.
New here? How to read a research record
- Start at the verdict. Every record states, before the experiment was run, what result would have made us abandon the idea. That is the "kill test". Then it says whether the test fired. Nothing gets reinterpreted after the fact.
- Numbers in square brackets are uncertainty.
23.4 [18.1, 28.7]means our best estimate is 23.4 and the true value is probably somewhere in that range. If a range includes zero, we cannot claim an effect. - Results that rule an idea out are kept. Roughly half of what is published here says an approach did not work, including plenty of our own. Those pages are the output, not a shortfall: knowing which direction is a dead end is what lets the next experiment go somewhere better, and most of what we now rely on came out of understanding why something else fell short. Work that only publishes what worked is not measuring anything.
- Read the Limits section. Every record ends with what it does not show. It is the most honest part of any experiment and usually the shortest.
- Pro tip: the figures near the top are designed to carry the result on their own. If you read nothing else, read the caption under each one, which says what it shows and what to take from it.
EXPLORATORY. Not a preregistered study. Local CPU, 96 training runs, no GPU, no cost. The design, precondition and kill test were committed (6f80341) before any run.
Program v2 Bucket O, item O3. Decisive computation: . Output: analysis/functional_transplant_fitted.py. Reproduce with analysis/functional_transplant_fitted.jsonpython analysis/functional_transplant_fitted.py (about twenty-five minutes on a throttled laptop CPU); --reuse re-derives every endpoint from the saved runs, and was checked to print the identical summary.
The question
J8 added a donor's leading gradient direction to a receiver's gradient for the 40 steps before the receiver's transition. The receiver's own direction speeds it up (-9.27 steps at half the gradient norm); a donor's slows it down (+4.72), no differently from a random direction drawn from the same donor (+5.72). An external review of the record proposed a reason: the parameter direction is specific to the model that produced it, while the functional change it causes -- what it does to the model's outputs -- might be shared. If so, J8's negative is a negative about coordinates, not about the thing itself.
O3 carries the function instead of the coordinates. On one probe batch of 256 sequences shared by every model, take what the donor's direction does to the donor's outputs; find the receiver update whose output change best reproduces it (minimum-norm least squares, 100 iterations of CGLS from zero, on the linearised model); normalise it to unit length; inject it exactly as J8 injects a direction.
Kill test, fixed before execution: functional minus functional-random (the donor's span-random direction carried the same way), paired over receivers, has an interval including zero -- exactly J8's test, one level up.
Precondition, fixed before execution: functional-self -- the receiver's own direction carried through the same fit -- must reproduce the self-transplant's known speed-up, or the fit cannot carry an established effect and the transfer arm is not interpreted.
Why this is the fourth attempt, and why this one could be trusted
Three earlier implementations (2026-09-01) each reimplemented J8's training loop and each got a different detail wrong: a perturbation on the parameters instead of the gradient, an intervention written after the optimiser step, and a live direction where J8 captures one. Each was caught by an arm anchor and none was interpreted. This attempt reimplements nothing. Every run is J8's own train(), imported. The model state the fit needs -- the receiver at the window's open, which train() does not return -- is obtained by running that same train() cut short at the step before the window and keeping the model its factory built.
The anchor, in code: all 32 re-run J8 arms (control, self-transplant, transplant, span-random on eight receivers) reproduce J8's committed transitions exactly.
Result: the kill test fires
Change in the receiver's transition (steps; negative is sooner), eight receivers, half the gradient norm, J8's transition definition (floor-to-ceiling midpoint, interpolated, 0.30 gate; every run passed it):
| Arm | What is injected | Change | Fit R² | Alignment with the receiver's gradient |
|---|---|---|---|---|
| self-transplant (J8) | the receiver's own direction | -9.27 [-17.36, -1.18] | 0.575 | |
| transplant (J8) | the donor's direction | +4.72 [+3.24, +6.19] | 0.046 | |
| span-random (J8) | a random donor direction | +5.72 [+3.85, +7.59] | 0.025 | |
| functional | the donor's output change, fitted | +11.45 [+8.62, +14.29] | 0.919 | 0.041 |
| functional-random | a random donor output change, fitted | +12.54 [+10.54, +14.53] | 0.913 | 0.016 |
| functional-self | the receiver's own output change, fitted | -8.56 [-16.22, -0.91] | 1.000 | 0.562 |
- The precondition passes: carried through the fit, the receiver's own direction still speeds it up (
-8.56against J8's-9.27). - The fit works: the receiver can reproduce
92%of the donor's output change (R²0.855-0.951per receiver) with an update whose cosine with the donor's parameter direction is0.007-- so the functional arm is genuinely a different vector from J8's, not the same one rediscovered. - And it does no better than a random donor's output change:
functionalminusfunctional-randomis-1.08[-4.03, +1.86]. The kill test fires.
What else the table says
- Carried functionally, a foreign step does twice the damage. Both fitted foreign arms delay about
12steps, against J8's5. A fitted update lives where the receiver's outputs are most sensitive, so a unit-length push in it moves the function further than a unit-length push in an arbitrary parameter direction. The fit made the intervention stronger and no more useful. - The only arm that helps is the one aligned with what the receiver was already doing. Self-transplant and functional-self both align at about
0.57with the receiver's own gradient; every foreign arm aligns at0.05or less. J8 read its self arm as a change of step size; this record supports that reading from a second direction.
What stands
- O3's hypothesis is answered, negatively. On this task and substrate, the functional change a donor's leading step produces does not transfer where its parameter direction did not. J8's negative is not a statement about coordinates.
- Set beside P1, the line is sharper. A donor's converged machinery transfers (P1,
-35.8steps); a donor's single step, carried as parameters or as function, does not. What travels is built structure, not a direction of travel. - The method of this record is the reusable part. Gate a re-test on reproducing the original exactly, and when the original's loop does not expose what you need, cut its loop short rather than writing a new one. Three attempts that rewrote the loop failed; the one that imported it did not.
Limits
- The positive control is close to the self arm itself. Fitted from its own output change, the receiver recovers its own direction almost exactly (cosine
0.98-1.00), sofunctional-selfvalidates the fitting and injection machinery rather than the ability to carry an effect across a change of coordinates. No arm here can show that ability, because the only effect known to exist is the receiver's own. - Linearised targets and fits at one probe batch; one scale (
alpha = 0.5); one task, one width (48), J8's eight receivers and eight donors, one donor per receiver as J8 assigned them. - One step's worth of function. A donor's accumulated change over a window, rather than one direction, is not tested here.
Terms on this page
Every piece of vocabulary this record uses, in plain language. Generated from the text above, so it cannot drift out of step with it.
- gradient
- The direction and amount by which each of a model's internal numbers should change to do slightly better. Training is repeatedly following it.
- kill test
- A condition written down before running the experiment that says what result would make us abandon the idea. Fixing it in advance is what stops a disappointing result being reinterpreted as an encouraging one.
- parameters
- The adjustable numbers inside a model. Training is the process of setting them. Model size is usually quoted as a count of these.
- probe
- A small separate model trained to read information out of a bigger model's internals, used as a measuring instrument rather than as a product.
- width
- How many internal numbers a model uses at each layer. The usual way we vary model size in these experiments.
Get new results as we publish them
Roughly monthly, one finding per email, in plain English first. Including the approaches that turned out not to work, which are usually the useful ones. No sales email.
Double opt-in: we send a confirmation link and add nobody who does not click it. One-click unsubscribe on every email.