Adapter algebra (v0.71.34)
soup adapters arithmetic applies task arithmetic (arXiv:2212.04089) to LoRA deltas. You add to blend two skills, scale to dial one down, and, the differentiator, negate to subtract one. The output is a single loadable adapter.
soup adapters arithmetic "coder + 0.5*math - toxic" \
--adapter coder=./coder-lora \
--adapter math=./math-lora \
--adapter toxic=./toxic-lora \
-o ./blendedNames in the expression map to adapter directories via the repeatable --adapter name=path. Exit 0 = ok, 1 = refusal.
Why the coefficient math matters
A LoRA does not store its delta directly; it stores two factors whose product is the effective weight change, ΔW = B @ A. Scale both factors by a coefficient c and the delta scales by c², which quietly breaks the whole idea: - toxic (c = -1) would square to +1 and become a no-op instead of a removal.
Soup splits the coefficient as the square root of its absolute value across the two factors and carries the sign, so the delta scales linearly: negation flips it, 0.5* halves it, 2* doubles it. The live check on two real rank-8 adapters: ||ΔW(2·a)|| / ||ΔW(a)|| = 2.000 exactly, where the old c² bug gives 4.0.
Be precise about how far that exactness goes: it holds on the self (diagonal) term, so scaling or negating a single adapter is exact. A multi-adapter sum (like the example above) also carries cross-terms between the adapters, which this element-wise combine does not cancel.
Mixed ranks are now exact (v0.72.0)
Mixed-rank inputs used to be refused with a "harmonize rank" message. As of v0.72.0 they are handled exactly, not approximated.
The reason it is exact is structural rather than numerical. A LoRA delta is ΔW = B @ A, so stacking the A factors of the inputs on top of each other and the coefficient-scaled B factors side by side gives a single adapter whose product is the sum of the individual deltas, by construction. Concatenation loses nothing; there is no approximation step to be wrong about. The output simply carries the summed rank.
# rank-8 and rank-16 adapters in one expression: no longer a refusal
soup adapters arithmetic "coder + 0.5*math" \
--adapter coder=./coder-r8 --adapter math=./math-r16 -o ./blended
# ask for a fixed output rank instead of the concatenated one
soup adapters arithmetic "coder + 0.5*math" \
--adapter coder=./coder-r8 --adapter math=./math-r16 --rank 16 -o ./blended--rank N truncates the concatenated result with an SVD, which is where an approximation does enter, deliberately and only when you ask for it: you are choosing a smaller adapter over an exact one. Without --rank, nothing is discarded.
Ranks are no longer a reason to refuse. Genuinely incompatible layer shapes still are.
What it refuses
| Guard | Behaviour |
|---|---|
| Mixed ranks | Handled exactly by concatenation since v0.72.0; --rank N truncates with an SVD if you want a fixed output rank |
| Different base models | Refused; --allow-cross-base overrides |
| A FAIL-scanned input | Refused by the backdoor-scan gate; --allow-unscanned skips it |
| Paths outside the working dir, symlinks | Rejected |
The combine is signed and un-normalized. Same-rank inputs combine element-wise; mixed-rank inputs combine by concatenation, which is exact by construction (v0.72.0).
The expression parser is hand-written. There is no eval, so an adapter name can never become code.
Honesty
What is proven is the mechanism: the delta really does negate, and the scaling ratio is exact to 2.000 on real adapters. What is not benchmarked is the behavioural outcome, so treat - toxic as "subtract that adapter's delta", not as a certified safety control. Whether subtracting a toxicity adapter measurably reduces toxicity on a real safety eval is an open question upstream calls proof-of-mechanism. Measure it on your own eval before you rely on it.
See also
- Adapter lifecycle — diff, merge (linear / TIES / DARE / SVD / CMA-ES), bisect, PR, lock.
- Adapters — the git-for-LoRA surface this builds on.
- Supply-chain security — the backdoor scan the gate reuses.
- LISA — the other half of the v0.71.34 release.
Soup is free and Apache-2.0. If it saved you a training run, starring the repo costs nothing and helps most. You can also fund the GPU time behind the work a 4 GB laptop cannot reach.