File size: 14,349 Bytes
c7cb9fe 7923ca5 c7cb9fe 7923ca5 794b73f 7923ca5 c7cb9fe 7923ca5 c7cb9fe 794b73f 7923ca5 794b73f 7923ca5 794b73f 7923ca5 c7cb9fe 7923ca5 794b73f c7cb9fe 7923ca5 794b73f 7923ca5 794b73f 0e022b5 c7cb9fe 7923ca5 794b73f 7923ca5 794b73f 7923ca5 794b73f 7923ca5 794b73f 7923ca5 794b73f 7923ca5 c7cb9fe 7923ca5 c7cb9fe 7923ca5 c7cb9fe 7923ca5 794b73f 7923ca5 794b73f 7923ca5 c7cb9fe 7923ca5 794b73f 7923ca5 0e022b5 7923ca5 794b73f c7cb9fe 0e022b5 c7cb9fe 794b73f 7923ca5 794b73f 7923ca5 794b73f 7923ca5 c7cb9fe | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 | #!/usr/bin/env python3
"""Layer-interleaved fusion of same-arch BF16 GGUFs (streaming).
Two donors (legacy): even blocks from A, odd blocks from B, globals from A.
fuse_layers.py --a A.gguf --b B.gguf --out OUT.gguf
Three donors (monster): per-block donor map + optional soup-averaged backbone.
fuse_layers.py --a OX.gguf --b ORN.gguf --c NEO.gguf --out M1.gguf \
--map "15:b,19:b,23:b,27:b,31:c"
Four donors: same, plus --d (e.g. --a M2.gguf --d MIMO.gguf --map "31:d").
Building from an existing fusion (e.g. RINIQ-M2-BF16) as --a is smart:
donor blocks already baked in, no need for the original BF16s.
fuse_layers.py --a OX.gguf --c NEO.gguf --out M3.gguf \
--map "15:b,19:b" --soup "0-8" --soup-from "a,c"
Rules: same trunk required (extras like MTP head in any donor are ignored,
globals always from A). Soup (weight averaging) only for backbone blocks
where donors are near-identical — never for divergent blocks.
"""
import argparse
import os
import re
import struct
import numpy as np
import gguf
from gguf.constants import GGUFValueType as VT
import sys
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from graft_mtp3 import pack_str, pack_field
def layer_of(name):
p = name.split(".")
if len(p) >= 2 and p[0] in ("blk", "BLK") and p[1].isdigit():
return int(p[1])
return None
def kind_of(name):
"""Tissue class of a tensor: ffn / attn / ssm / norm / glob.
Same split as the weight/imatrix compasses: the soul lives in FFN
(weight divergence ~2x attention), compatibility in attention.
"""
p = name.split(".")
if len(p) < 3 or p[0] not in ("blk", "BLK"):
return "glob"
rest = ".".join(p[2:])
if "ffn" in rest:
return "ffn"
if "attn" in rest:
return "attn"
if "ssm" in rest:
return "ssm"
return "norm"
def parse_blocks(spec):
"""'0-8,11,15' -> sorted set of ints."""
out = set()
for part in spec.split(","):
part = part.strip()
if not part:
continue
m = re.fullmatch(r"(\d+)-(\d+)", part)
if m:
lo, hi = int(m.group(1)), int(m.group(2))
out.update(range(min(lo, hi), max(lo, hi) + 1))
else:
out.add(int(part))
return out
def parse_map(spec):
"""'15:b,31:c' -> {15: ('b', None), 31: ('c', None)} (whole block).
Tissue mode: '15:b:ffn' -> {15: ('b', {'ffn'})} — only FFN-kind tensors
of block 15 come from B, the rest of the block stays on A.
Ranges: '15-17:b:ffn' expands to 15,16,17. Multi-tissue: '15:b:ffn+attn'.
Kinds: ffn / attn / ssm / norm (see kind_of) + ln (any *norm* tensor,
e.g. attn_norm/post_attention_norm/ssm_norm — the gain staging)."""
out = {}
for part in spec.split(","):
part = part.strip()
if not part:
continue
segs = [s.strip() for s in part.split(":")]
blk_spec, donor = segs[0], segs[1].lower() if len(segs) > 1 else "a"
assert donor in ("a", "b", "c", "d"), "map donor must be a/b/c/d: %r" % part
kinds = None
if len(segs) > 2 and segs[2]:
kinds = frozenset(k.strip().lower() for k in segs[2].split("+"))
assert kinds <= {"ffn", "attn", "ssm", "norm", "ln"}, \
"map kinds must be ffn/attn/ssm/norm/ln: %r" % part
m = re.fullmatch(r"(\d+)-(\d+)", blk_spec)
blks = range(min(int(m.group(1)), int(m.group(2))),
max(int(m.group(1)), int(m.group(2))) + 1) if m \
else (int(blk_spec),)
for blk in blks:
out[blk] = (donor, kinds)
return out
def soup_average(blobs, tensor_type):
"""Average raw tensor blobs across donors. BF16 decoded exactly."""
from gguf.constants import GGMLQuantizationType as GQ
if int(tensor_type) == int(GQ.BF16):
import ml_dtypes
acc = None
for b in blobs:
f = np.frombuffer(b, dtype=ml_dtypes.bfloat16).astype(np.float32)
acc = f if acc is None else acc + f
acc /= len(blobs)
return acc.astype(ml_dtypes.bfloat16).tobytes()
# F32 and friends: plain average
acc = None
for b in blobs:
f = np.frombuffer(b, dtype=np.float32).astype(np.float64)
acc = f if acc is None else acc + f
acc /= len(blobs)
return acc.astype(np.float32).tobytes()
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--a", required=True)
ap.add_argument("--b", required=True)
ap.add_argument("--c", default=None, help="third donor (monster mode)")
ap.add_argument("--d", default=None, help="fourth donor (e.g. MiMo blk31 onto M2 base)")
ap.add_argument("--out", required=True)
ap.add_argument("--swap", default=None,
help="comma list of block ids to take from B "
"(default: odd blocks from B, even from A)")
ap.add_argument("--map", default=None,
help="block->donor map, e.g. '15:b,19:b,31:c' "
"(donors a/b/c/d; overrides --swap for listed blocks). "
"TISSUE mode: '15:b:ffn' grafts only FFN-kind tensors "
"of block 15 (kinds: ffn/attn/ssm/norm, '+' for several; "
"'15-17:b:ffn' for ranges), rest stays on A")
ap.add_argument("--soup", default=None,
help="blocks to weight-average, e.g. '0-8,11' "
"(backbone only — never divergent blocks)")
ap.add_argument("--soup-from", default=None,
help="donors to average, e.g. 'a,c' (default: all given)")
ap.add_argument("--dry", action="store_true",
help="print common trunk + donor map and exit "
"(no 18GB write)")
ap.add_argument("--expect", default=None,
help="gate the resolved donor map, e.g. "
"'15:b,16:b,17:b,31:c' (normalised like --map: "
"ranges expanded, donors/kinds lowercased, kinds "
"sorted; tissue '15-17:b:ffn' allowed). Exits "
"non-zero with a diff on any mismatch, before a "
"single output byte is written. Silent when unused.")
args = ap.parse_args()
donors = {"a": gguf.GGUFReader(args.a), "b": gguf.GGUFReader(args.b)}
if args.c:
donors["c"] = gguf.GGUFReader(args.c)
if args.d:
donors["d"] = gguf.GGUFReader(args.d)
T = {k: {t.name: t for t in r.tensors} for k, r in donors.items()}
ta = T["a"]
# extras may sit on either side (e.g. MTP head in A, or in B):
# fuse the common trunk, extras always come from A
common = set(ta)
for k in list(T):
if k == "a":
continue
common &= set(T[k])
print("common trunk: %d tensors (%d extras stay from A)" %
(len(common), len(ta) - len(common)), flush=True)
# shape gate across all donors (common trunk only)
for n, t in ta.items():
if n not in common:
continue
for k in T:
if k == "a":
continue
assert [int(d) for d in T[k][n].shape] == [int(d) for d in t.shape], \
"shape mismatch %s: a vs %s" % (n, k)
mmap = parse_map(args.map) if args.map else {}
for blk, (d, _k) in mmap.items():
assert d in donors, "map donor %r has no file (pass --%s)" % (d, d)
soupset = parse_blocks(args.soup) if args.soup else set()
sfrom = [s.strip().lower() for s in args.soup_from.split(",")] if args.soup_from \
else sorted(donors)
for d in sfrom:
assert d in donors, "soup donor %r has no file" % d
assert not (soupset & set(mmap)), "block both in --map and --soup: %s" % \
sorted(soupset & set(mmap))
parts = []
ra = donors["a"]
for k, f in ra.fields.items():
if k.startswith("GGUF."):
continue
b = pack_field(k, f)
if b is not None:
parts.append(b)
kv_raw = b"".join(parts)
from gguf.constants import GGMLQuantizationType as GQ
ti_raw, off, order = b"", 0, []
swapset = set(int(x) for x in args.swap.split(",")) if args.swap else None
donor_of_block = {}
for name in [t.name for t in ra.tensors]:
lyr = layer_of(name)
if lyr is None:
donor, soup = "a", False # globals: always from A
elif lyr in soupset:
donor, soup = None, True
elif lyr in mmap:
md, mk = mmap[lyr]
if mk is None or kind_of(name) in mk or \
("ln" in mk and "norm" in name):
donor, soup = md, False
else:
donor, soup = "a", False # tissue: rest of block stays home
elif swapset is not None:
donor, soup = ("b" if lyr in swapset else "a"), False
elif mmap:
# explicit --map means base-A-plus-overrides (scar 2026-09-23:
# N1/N1m passed --map with 2 donors and silently got the legacy
# odd/even interleave instead of a base — half-foreign models
# misread as 3-block grafts). Unmapped blocks stay on A.
donor, soup = "a", False
elif len(donors) == 2:
donor, soup = (("b" if (lyr or 0) % 2 == 1 else "a")), False
else:
# unmapped blocks default to the base (A) — never surprise-mix.
# explicit tri-interleave via a full --map if wanted.
donor, soup = "a", False
if lyr is not None:
donor_of_block.setdefault(lyr, donor if not soup else "soup(%s)" % "+".join(sfrom))
t = ta[name]
ti_raw += pack_str(name)
dims = [int(d) for d in t.shape]
ti_raw += struct.pack("<I", len(dims))
for d in dims:
ti_raw += struct.pack("<Q", d)
ti_raw += struct.pack("<I", int(t.tensor_type))
ti_raw += struct.pack("<Q", off)
ne = 1
for d in dims:
ne *= d
bpe = 2 if int(t.tensor_type) == int(GQ.BF16) else 4
off += ne * bpe
off += (32 - off % 32) % 32
order.append((name, donor, soup))
for blk, (md, mk) in mmap.items():
if mk is not None and blk in donor_of_block:
donor_of_block[blk] = "%s(%s-only)" % (md, "+".join(sorted(mk)))
print("donor map: " + ", ".join(
"%d:%s" % (b, donor_of_block[b]) for b in sorted(donor_of_block)), flush=True)
if args.expect is not None:
# scar 2026-09-23: N1/N1m --map silently fell back to interleave and
# nobody diffed the printed map against the recipe. --expect turns
# that printed line into a gate: every expected block must be
# EXPLICIT in --map and resolve to the expected donor/tissue.
exp = parse_map(args.expect)
def canon(blk, dk):
d, k = dk
return "%d:%s%s" % (blk, d, (":" + "+".join(sorted(k))) if k else "")
errs = []
for b in sorted(exp):
if b not in mmap:
errs.append("block %d: expected %s but not listed in --map "
"(would be silent fallback)" % (b, canon(b, exp[b])))
elif b not in donor_of_block:
errs.append("block %d: expected %s but block absent from "
"base model" % (b, canon(b, exp[b])))
else:
ed, ek = exp[b]
want = ed if ek is None else \
"%s(%s-only)" % (ed, "+".join(sorted(ek)))
if donor_of_block[b] != want:
errs.append("block %d: expected %s, resolved %s"
% (b, want, donor_of_block[b]))
for b in sorted(set(mmap) - set(exp)):
errs.append("extra --map block not in --expect: %s" % canon(b, mmap[b]))
if errs:
print("EXPECT MISMATCH (%d):" % len(errs), flush=True)
for e in errs:
print(" " + e, flush=True)
sys.exit(2)
print("expect: map matches (%d blocks)" % len(exp), flush=True)
print("globals: a, trunk tensors: %d" % len(ta), flush=True)
if args.dry:
print("dry run: no output written", flush=True)
return
n_tensors = len(ta)
hdr = b"GGUF" + struct.pack("<I", 3) + struct.pack("<Q", n_tensors)
hdr += struct.pack("<Q", len(parts))
hdr += kv_raw + ti_raw
hdr += b"\x00" * ((32 - len(hdr) % 32) % 32)
print("header %d bytes" % len(hdr), flush=True)
# SOUP SHAPE GUARD (the trunk gate at line ~176 covers every tensor
# present in ALL donors by exact shape — including the cross-arch case,
# which fails loudly there, never a silent corrupt file. The one hole:
# a soup tensor missing from some donor is NOT in the common trunk, so
# its holders were never shape-checked and soup_average would broadcast
# garbage. Fail here, before a single output byte is written.)
def _ne(t):
n = 1
for d in t.shape:
n *= int(d)
return n
for name, _donor, _soup in order:
if not _soup:
continue
want = _ne(ta[name])
holders = [d for d in sfrom if name in T[d]] or ["a"]
for d in holders:
have = _ne(T[d][name])
assert have == want, \
"shape mismatch: soup %s from %s has %d elements, " \
"base has %d" % (name, d, have, want)
with open(args.out, "wb") as fout:
fout.write(hdr)
# stream per tensor via reader mmap slices (soup averaged in RAM;
# extras missing from a donor fall back to A)
for name, donor, soup in order:
if soup:
holders = [d for d in sfrom if name in T[d]] or ["a"]
blobs = [np.ascontiguousarray(T[d][name].data).tobytes()
for d in holders]
raw = soup_average(blobs, ta[name].tensor_type)
else:
src = T[donor][name] if name in T[donor] else ta[name]
raw = np.ascontiguousarray(src.data).tobytes()
fout.write(raw)
pad = (32 - len(raw) % 32) % 32
fout.write(b"\x00" * pad)
print("Done:", os.path.getsize(args.out), flush=True)
if __name__ == "__main__":
main()
|