rad-convert's GPTQ path reads importance from an imatrix GGUF; the reference container's --kld-record pass emits per-expert and per-hidden Grams as raw recordings. This folds them into one v3 file: a single expert-major f32 entry per MoE layer plus the hidden-width entries, every data offset a multiple of general.alignment as the loader requires. Refusals, each one a silent-worse outcome the swift15 campaign hit: an expert with no gram of its own carries the layer's pooled diagonal and the count is reported (a silent zero is a down-quantized expert nobody priced); expert grams with no pooled gram, a gram short by one float (names the file), the same global expert recorded twice with different numbers, an empty recording directory, and an existing output. --min-rows sets the noise floor below which a recording is dropped. rows is u64 so a corpus that outgrows u32 survives the header instead of wrapping.
446 lines
19 KiB
Bash
Executable File
446 lines
19 KiB
Bash
Executable File
#!/usr/bin/env bats
|
|
# Seam: scripts/grams-to-imatrix.py — the calibration recording (RADGRAM2 grams)
|
|
# folded into the GGUF v3 imatrix `rad-convert --imatrix F` reads.
|
|
#
|
|
# The only contract that matters is the reader's: core/format/imatrix.cpp joins
|
|
# `<base>.in_sum2` with `<base>.counts`, takes cols from in_sum2 dims[0] and
|
|
# n_expert from dims[1], demands ggml f32 tensors and GGUF version 3, and
|
|
# silently IGNORES an entry whose width does not match the weight it is asked
|
|
# about. So every test here parses the artifact back with its own tiny GGUF
|
|
# reader and asserts bytes — a file that merely exists and exits 0 proves
|
|
# nothing about a quantiser that will read it hours later on another machine.
|
|
#
|
|
# Fixtures are synthetic: k=4 grams with real RADGRAM2 headers (rows is a u64
|
|
# at offset 16 — pack it as u32 and 100 rows read back as 4294967296000) and
|
|
# real f32 payloads whose OFF-DIAGONAL is filled with 99.0, so an entry built
|
|
# from anything but the diagonal is instantly visible. Expected numbers are
|
|
# worked examples from the fixture tables below, never recomputed the way the
|
|
# tool computes them.
|
|
|
|
load helpers
|
|
|
|
TOOL="$REPO_ROOT/scripts/grams-to-imatrix.py"
|
|
|
|
# make_gram <path> <k> <rank> <rows> <domain> <diag,csv>
|
|
# A RADGRAM2 file: 32-byte header <8sIIQI (magic, k, rank, rows, domain, pad)
|
|
# then k*k f32 row-major. Off-diagonals are 99.0 — a sentinel, not data.
|
|
make_gram() {
|
|
python3 - "$@" <<'PY'
|
|
import struct, sys
|
|
path, k, rank, rows, domain = sys.argv[1], int(sys.argv[2]), int(sys.argv[3]), \
|
|
int(sys.argv[4]), int(sys.argv[5])
|
|
diag = [float(x) for x in sys.argv[6].split(",")]
|
|
assert len(diag) == k, "diagonal must hold k values"
|
|
vals = [99.0] * (k * k)
|
|
for i, d in enumerate(diag):
|
|
vals[i * k + i] = d
|
|
hdr = struct.pack("<8sIIQI", b"RADGRAM2", k, rank, rows, domain) + struct.pack("<I", 0)
|
|
with open(path, "wb") as f:
|
|
f.write(hdr + struct.pack("<%df" % (k * k), *vals))
|
|
PY
|
|
}
|
|
|
|
# full_recording <dir> — the fixture every happy-path test reads.
|
|
#
|
|
# layer 0 (k=4, own-gram floor 4*k = 16 rows):
|
|
# pooled r0 rows 40 diag 1,2,3,4 + r1 rows 8 diag .5,.5,.5,.5
|
|
# -> summed gram (the engine's calib_sum law): diag 1.5,2.5,3.5,4.5,
|
|
# pooled rows 48
|
|
# expert 0 r0 rows 100 diag 10,20,30,40 (>= 16: own gram kept)
|
|
# expert 1 r0 rows 3 diag 7,7,7,7 (< 16: inherits pooled)
|
|
# expert 2 r1 rows 64 diag 1,1,1,2 (>= 16: own gram kept)
|
|
# expert 3 no file at all (inherits pooled, count 0)
|
|
# expert 4 r1 rows 100 diag 5,6,7,8 (>= 16: own gram kept)
|
|
# -> n_expert 5, counts 100 3 64 0 100, inherited 2, routing 267
|
|
# layer 1: pooled r0 rows 48 diag 2,2,2,2 ; expert 0 on r1 rows 48 diag 1,1,1,1
|
|
# -> n_expert 1, counts 48, inherited 0, routing 48
|
|
# Corpus rows = the largest single-layer pooled rows = 48.
|
|
full_recording() {
|
|
local d="$1"
|
|
mkdir -p "$d"
|
|
make_gram "$d/gram.r0.blk.0.ffn_down_exps.bin" 4 0 40 0 "1,2,3,4"
|
|
make_gram "$d/gram.r1.blk.0.ffn_down_exps.bin" 4 1 8 0 "0.5,0.5,0.5,0.5"
|
|
make_gram "$d/gram.r0.blk.0.ffn_down_exps.0.bin" 4 0 100 0 "10,20,30,40"
|
|
make_gram "$d/gram.r0.blk.0.ffn_down_exps.1.bin" 4 0 3 0 "7,7,7,7"
|
|
make_gram "$d/gram.r1.blk.0.ffn_down_exps.2.bin" 4 1 64 0 "1,1,1,2"
|
|
make_gram "$d/gram.r1.blk.0.ffn_down_exps.4.bin" 4 1 100 0 "5,6,7,8"
|
|
make_gram "$d/gram.r0.blk.1.ffn_down_exps.bin" 4 0 48 0 "2,2,2,2"
|
|
make_gram "$d/gram.r1.blk.1.ffn_down_exps.0.bin" 4 1 48 0 "1,1,1,1"
|
|
}
|
|
|
|
# gguf_read <file> [vals <tensor> <float-index> <count>] — an independent GGUF
|
|
# v3 reader: KV block, tensor infos, and (on request) the f32 values behind a
|
|
# tensor's own declared offset. Prints KV/HDR/TENSOR lines. Nothing here shares
|
|
# code with the writer, which is the only reason the byte assertions mean
|
|
# anything: imatrix.cpp walks the same three structures.
|
|
gguf_read() {
|
|
python3 - "$@" <<'PY'
|
|
import struct, sys
|
|
d = open(sys.argv[1], "rb").read()
|
|
def u32(o): return struct.unpack_from("<I", d, o)[0]
|
|
def u64(o): return struct.unpack_from("<Q", d, o)[0]
|
|
def st(o):
|
|
n = u64(o)
|
|
return d[o + 8:o + 8 + n].decode(), o + 8 + n
|
|
assert d[:4] == b"GGUF", "magic is %r" % d[:4]
|
|
ver, n_t, n_kv = u32(4), u64(8), u64(16)
|
|
p, align, out = 24, 32, []
|
|
for _ in range(n_kv):
|
|
key, p = st(p)
|
|
ty = u32(p); p += 4
|
|
if ty == 4: # uint32
|
|
v = u32(p); p += 4
|
|
if key == "general.alignment":
|
|
align = v
|
|
elif ty == 8: # string
|
|
v, p = st(p)
|
|
elif ty == 9: # array
|
|
et = u32(p); n = u64(p + 4); p += 12
|
|
got = []
|
|
for _i in range(n):
|
|
if et != 8:
|
|
raise SystemExit("kv array element type %d" % et)
|
|
s, p = st(p)
|
|
got.append(s)
|
|
v = ",".join(got)
|
|
else:
|
|
raise SystemExit("kv type %d is not steppable" % ty)
|
|
out.append("KV %s %s" % (key, v))
|
|
tens = []
|
|
for _ in range(n_t):
|
|
name, p = st(p)
|
|
nd = u32(p); p += 4
|
|
dims = [u64(p + 8 * i) for i in range(nd)]; p += 8 * nd
|
|
ty = u32(p); off = u64(p + 4); p += 12
|
|
tens.append((name, dims, ty, off))
|
|
ds = (p + align - 1) // align * align
|
|
out.append("HDR version=%d n_tensors=%d n_kv=%d alignment=%d data_start=%d"
|
|
% (ver, n_t, n_kv, align, ds))
|
|
for name, dims, ty, off in tens:
|
|
out.append("TENSOR %s %s %d %d" % (name, "x".join(str(x) for x in dims), ty, off))
|
|
if len(sys.argv) > 2 and sys.argv[2] == "vals":
|
|
name, i0, n = sys.argv[3], int(sys.argv[4]), int(sys.argv[5])
|
|
for nm, dims, ty, off in tens:
|
|
if nm == name:
|
|
base = ds + off
|
|
vals = [("%d" % v) if float(v).is_integer() else repr(v)
|
|
for v in (struct.unpack_from("<f", d, base + 4 * (i0 + i))[0]
|
|
for i in range(n))]
|
|
out.append("VALS %s %s" % (name, " ".join(vals)))
|
|
print("\n".join(out))
|
|
PY
|
|
}
|
|
|
|
# aligned_offsets <file> — exit 0 iff the data section starts aligned AND every
|
|
# tensor's absolute data offset (data_start + t.off) is a multiple of
|
|
# general.alignment. This is the writer bug that no value assertion can see: an
|
|
# unaligned offset makes the reader's extent arithmetic land mid-float in the
|
|
# previous tensor, and it reads the result without complaining.
|
|
aligned_offsets() {
|
|
python3 - "$1" <<'PY'
|
|
import struct, sys
|
|
d = open(sys.argv[1], "rb").read()
|
|
def u32(o): return struct.unpack_from("<I", d, o)[0]
|
|
def u64(o): return struct.unpack_from("<Q", d, o)[0]
|
|
def st(o):
|
|
n = u64(o)
|
|
return d[o + 8:o + 8 + n].decode(), o + 8 + n
|
|
ver, n_t, n_kv = u32(4), u64(8), u64(16)
|
|
p, align = 24, 32
|
|
for _ in range(n_kv):
|
|
key, p = st(p)
|
|
ty = u32(p); p += 4
|
|
if ty == 4:
|
|
v = u32(p); p += 4
|
|
if key == "general.alignment":
|
|
align = v
|
|
if ty == 8:
|
|
_v, p = st(p)
|
|
elif ty == 9:
|
|
et = u32(p); n = u64(p + 4); p += 12
|
|
for _i in range(n):
|
|
if et == 8:
|
|
_v, p = st(p)
|
|
elif et == 4:
|
|
p += 4
|
|
else:
|
|
raise SystemExit("kv array element type %d" % et)
|
|
elif ty in (0, 1):
|
|
p += 1
|
|
elif ty in (2, 3):
|
|
p += 2
|
|
elif ty in (5, 6):
|
|
p += 4
|
|
elif ty in (10, 11, 12):
|
|
p += 8
|
|
tens = []
|
|
for _ in range(n_t):
|
|
name, p = st(p)
|
|
nd = u32(p); p += 4
|
|
p += 8 * nd
|
|
ty = u32(p); off = u64(p + 4); p += 12
|
|
tens.append((name, ty, off))
|
|
ds = (p + align - 1) // align * align
|
|
bad = [] if ds % align == 0 else ["data_start=%d" % ds]
|
|
bad += ["%s=%d" % (nm, ds + off) for nm, _ty, off in tens if (ds + off) % align]
|
|
if bad:
|
|
print("UNALIGNED", ";".join(bad))
|
|
sys.exit(1)
|
|
print("ALIGNED %d tensor(s), data_start=%d, alignment=%d" % (len(tens), ds, align))
|
|
PY
|
|
}
|
|
|
|
# --- the artifact -----------------------------------------------------------
|
|
|
|
@test "a complete recording becomes a GGUF v3 file and the run reports itself" {
|
|
C="$BATS_TEST_TMPDIR/full"
|
|
full_recording "$C"
|
|
run python3 "$TOOL" "$C" -o "$BATS_TEST_TMPDIR/im.gguf" \
|
|
--datasets calib,chat --chunk-size 16
|
|
[ "$status" -eq 0 ]
|
|
[[ "$output" == *"IMATRIX_OK"* ]]
|
|
head -c 4 "$BATS_TEST_TMPDIR/im.gguf" | grep -q "GGUF"
|
|
run gguf_read "$BATS_TEST_TMPDIR/im.gguf"
|
|
[[ "$output" == *"HDR version=3"* ]]
|
|
# The reader refuses anything but version 3, so this is the whole gate.
|
|
[[ "$output" != *"version=2"* ]]
|
|
# Everything the reader looks for by key, and the honesty of the chunk math:
|
|
# 48 corpus rows over 16-row chunks is 3 chunks.
|
|
[[ "$output" == *"KV general.alignment 32"* ]]
|
|
[[ "$output" == *"KV imatrix.chunk_size 16"* ]]
|
|
[[ "$output" == *"KV imatrix.chunk_count 3"* ]]
|
|
[[ "$output" == *"KV imatrix.datasets calib,chat"* ]]
|
|
[[ "$output" == *"n_tensors=12"* ]]
|
|
}
|
|
|
|
@test "the reader finds the folded MoE entry: one per layer, expert-major, f32" {
|
|
C="$BATS_TEST_TMPDIR/full"
|
|
full_recording "$C"
|
|
run python3 "$TOOL" "$C" -o "$BATS_TEST_TMPDIR/im.gguf"
|
|
[ "$status" -eq 0 ]
|
|
run gguf_read "$BATS_TEST_TMPDIR/im.gguf"
|
|
# dims (k, n_expert) with k fastest, ggml type 0 (f32) or the entry is
|
|
# warned away, and 5 experts because the fixture's largest global id is 4.
|
|
[[ "$output" == *"TENSOR blk.0.ffn_down_exps.in_sum2 4x5 0 "* ]]
|
|
[[ "$output" == *"TENSOR blk.0.ffn_down_exps.counts 1x5 0 "* ]]
|
|
# counts are the routing rows, not the energy trace: 100 3 64 0 100.
|
|
run gguf_read "$BATS_TEST_TMPDIR/im.gguf" vals blk.0.ffn_down_exps.counts 0 5
|
|
[[ "$output" == *"VALS blk.0.ffn_down_exps.counts 100 3 64 0 100"* ]]
|
|
# Expert 0 (floats 0..3) and expert 4 (floats 16..19) carry their own
|
|
# matrices' diagonals; the 99.0 off-diagonal fill appears nowhere.
|
|
run gguf_read "$BATS_TEST_TMPDIR/im.gguf" vals blk.0.ffn_down_exps.in_sum2 0 4
|
|
[[ "$output" == *"VALS blk.0.ffn_down_exps.in_sum2 10 20 30 40"* ]]
|
|
run gguf_read "$BATS_TEST_TMPDIR/im.gguf" vals blk.0.ffn_down_exps.in_sum2 16 4
|
|
[[ "$output" == *"VALS blk.0.ffn_down_exps.in_sum2 5 6 7 8"* ]]
|
|
[[ "$output" != *"99"* ]]
|
|
}
|
|
|
|
@test "an expert with no own gram carries the pooled diagonal, and the count of them is reported" {
|
|
C="$BATS_TEST_TMPDIR/full"
|
|
full_recording "$C"
|
|
run python3 "$TOOL" "$C" -o "$BATS_TEST_TMPDIR/im.gguf"
|
|
[ "$status" -eq 0 ]
|
|
[[ "$output" == *"inherited=2"* ]]
|
|
# Expert 1 has a gram (3 rows, under the 4*k floor) and expert 3 has none at
|
|
# all; both slices must be the POOLED diag 1+0.5, 2+0.5, 3+0.5, 4+0.5.
|
|
run gguf_read "$BATS_TEST_TMPDIR/im.gguf" vals blk.0.ffn_down_exps.in_sum2 4 4
|
|
[[ "$output" == *"VALS blk.0.ffn_down_exps.in_sum2 1.5 2.5 3.5 4.5"* ]]
|
|
run gguf_read "$BATS_TEST_TMPDIR/im.gguf" vals blk.0.ffn_down_exps.in_sum2 12 4
|
|
[[ "$output" == *"VALS blk.0.ffn_down_exps.in_sum2 1.5 2.5 3.5 4.5"* ]]
|
|
# Their counts stay their own routing: 3 for the under-threshold expert, 0
|
|
# for the unrecorded one — popularity is count's job, never the gram's.
|
|
run gguf_read "$BATS_TEST_TMPDIR/im.gguf" vals blk.0.ffn_down_exps.counts 1 3
|
|
[[ "$output" == *"VALS blk.0.ffn_down_exps.counts 3 64 0"* ]]
|
|
# The census is a report for the operator, so it belongs on stderr. `run`
|
|
# folds both streams into $output, so a second run into a different -o (an
|
|
# existing output is refused by design) captures stderr alone: without the
|
|
# stderr write there is no file to grep and this assertion goes red.
|
|
python3 "$TOOL" "$C" -o "$BATS_TEST_TMPDIR/im2.gguf" 2>"$BATS_TEST_TMPDIR/err"
|
|
[ -s "$BATS_TEST_TMPDIR/err" ]
|
|
grep -q "inherited=2" "$BATS_TEST_TMPDIR/err"
|
|
}
|
|
|
|
@test "the hidden-width entries are the same bytes, and nothing else is emitted" {
|
|
C="$BATS_TEST_TMPDIR/full"
|
|
full_recording "$C"
|
|
run python3 "$TOOL" "$C" -o "$BATS_TEST_TMPDIR/im.gguf"
|
|
[ "$status" -eq 0 ]
|
|
run gguf_read "$BATS_TEST_TMPDIR/im.gguf"
|
|
# gate_up shares the down matrices (same hidden-state rows), so its slice 4
|
|
# must read 5 6 7 8 at its own tensor info's offset.
|
|
[[ "$output" == *"TENSOR blk.0.ffn_gate_up_exps.in_sum2 4x5 0 "* ]]
|
|
[[ "$output" == *"TENSOR blk.0.ssm_inz.in_sum2 4 0 "* ]]
|
|
[[ "$output" == *"TENSOR blk.0.ssm_inz.counts 1 0 "* ]]
|
|
run gguf_read "$BATS_TEST_TMPDIR/im.gguf" vals blk.0.ffn_gate_up_exps.in_sum2 16 4
|
|
[[ "$output" == *"VALS blk.0.ffn_gate_up_exps.in_sum2 5 6 7 8"* ]]
|
|
run gguf_read "$BATS_TEST_TMPDIR/im.gguf" vals blk.0.ssm_inz.in_sum2 0 4
|
|
[[ "$output" == *"VALS blk.0.ssm_inz.in_sum2 1.5 2.5 3.5 4.5"* ]]
|
|
run gguf_read "$BATS_TEST_TMPDIR/im.gguf" vals blk.0.ssm_inz.counts 0 1
|
|
[[ "$output" == *"VALS blk.0.ssm_inz.counts 48"* ]]
|
|
# The stop rule: importance_for() false-on-a-width-mismatch means an entry
|
|
# for a projection whose input is NOT the gram's width is a silent lie.
|
|
[[ "$output" != *"ssm_out"* ]]
|
|
[[ "$output" != *"attn"* ]]
|
|
[[ "$output" != *"mtp."* ]]
|
|
[[ "$output" != *"ple_ngram"* ]]
|
|
[[ "$output" != *"output.weight"* ]]
|
|
}
|
|
|
|
@test "rows is a u64: a corpus too big for u32 survives the header" {
|
|
# The trap this fixture law exists for: pack rows as u32 at offset 16 and a
|
|
# 100-row file reads back as 4294967296000. 2**32 is exactly representable
|
|
# in f32, so the count round-trips only if the tool read 8 bytes.
|
|
C="$BATS_TEST_TMPDIR/wide"
|
|
mkdir -p "$C"
|
|
make_gram "$C/gram.r0.blk.0.ffn_down_exps.bin" 4 0 4294967296 0 "1,1,1,1"
|
|
make_gram "$C/gram.r0.blk.0.ffn_down_exps.0.bin" 4 0 4294967296 0 "3,3,3,3"
|
|
run python3 "$TOOL" "$C" -o "$BATS_TEST_TMPDIR/wide.gguf" --chunk-size 4096
|
|
[ "$status" -eq 0 ]
|
|
run gguf_read "$BATS_TEST_TMPDIR/wide.gguf" vals blk.0.ffn_down_exps.counts 0 1
|
|
[[ "$output" == *"VALS blk.0.ffn_down_exps.counts 4294967296"* ]]
|
|
[[ "$output" != *"4294967296000"* ]]
|
|
run gguf_read "$BATS_TEST_TMPDIR/wide.gguf"
|
|
[[ "$output" == *"KV imatrix.chunk_count 1048576"* ]]
|
|
# imatrix.chunk_count is a u32 field, so 2**32 rows in 1-row chunks has no
|
|
# legal encoding: wrapping it would put a number downstream believes.
|
|
run python3 "$TOOL" "$C" -o "$BATS_TEST_TMPDIR/wide2.gguf" --chunk-size 1
|
|
[ "$status" -eq 2 ]
|
|
[[ "$output" == *"REFUSED"*"CHUNK_COUNT"* ]]
|
|
[ ! -e "$BATS_TEST_TMPDIR/wide2.gguf" ]
|
|
}
|
|
|
|
@test "every tensor's data offset is a multiple of general.alignment" {
|
|
C="$BATS_TEST_TMPDIR/full"
|
|
full_recording "$C"
|
|
run python3 "$TOOL" "$C" -o "$BATS_TEST_TMPDIR/im.gguf"
|
|
[ "$status" -eq 0 ]
|
|
run aligned_offsets "$BATS_TEST_TMPDIR/im.gguf"
|
|
[ "$status" -eq 0 ]
|
|
[[ "$output" == "ALIGNED 12 tensor(s),"*"alignment=32" ]]
|
|
|
|
# Red proof, in the same test so the assertion cannot rot: the same file
|
|
# with one tensor's declared offset nudged by 4. A counts tensor is 4 bytes,
|
|
# so a writer that forgets to pad between tensors produces exactly this
|
|
# shape of file — and every value assertion above still passes on it.
|
|
python3 - "$BATS_TEST_TMPDIR/im.gguf" "$BATS_TEST_TMPDIR/unaligned.gguf" <<'PY'
|
|
import struct, sys
|
|
src, dst = sys.argv[1], sys.argv[2]
|
|
d = bytearray(open(src, "rb").read())
|
|
def u32(o): return struct.unpack_from("<I", d, o)[0]
|
|
def u64(o): return struct.unpack_from("<Q", d, o)[0]
|
|
def st(o):
|
|
n = u64(o)
|
|
return d[o + 8:o + 8 + n].decode(), o + 8 + n
|
|
p = 24
|
|
for _ in range(u64(16)):
|
|
key, p = st(p)
|
|
ty = u32(p); p += 4
|
|
if ty == 4:
|
|
p += 4
|
|
elif ty == 8:
|
|
_v, p = st(p)
|
|
elif ty == 9:
|
|
et = u32(p); n = u64(p + 4); p += 12
|
|
for _i in range(n):
|
|
_v, p = st(p)
|
|
for _ in range(u64(8)):
|
|
name, p = st(p)
|
|
nd = u32(p); p += 4
|
|
p += 8 * nd
|
|
off_at = p + 4 # the u64 offset field of this tensor
|
|
if name == "blk.0.ssm_inz.counts":
|
|
struct.pack_into("<Q", d, off_at, u64(off_at) + 4)
|
|
p += 12
|
|
open(dst, "wb").write(bytes(d))
|
|
PY
|
|
run aligned_offsets "$BATS_TEST_TMPDIR/unaligned.gguf"
|
|
[ "$status" -eq 1 ]
|
|
[[ "$output" == *"UNALIGNED"*"blk.0.ssm_inz.counts"* ]]
|
|
}
|
|
|
|
# --- the refusals -----------------------------------------------------------
|
|
|
|
@test "a gram short by one float is refused and the file is named" {
|
|
C="$BATS_TEST_TMPDIR/short"
|
|
full_recording "$C"
|
|
# Cut the last 4 bytes of one expert gram: a torn write, which is what a
|
|
# killed tap actually leaves behind.
|
|
p="$C/gram.r0.blk.0.ffn_down_exps.0.bin"
|
|
python3 -c 'import sys
|
|
p = sys.argv[1]
|
|
open(p, "wb").write(open(p, "rb").read()[:-4])' "$p"
|
|
run python3 "$TOOL" "$C" -o "$BATS_TEST_TMPDIR/no.gguf"
|
|
[ "$status" -eq 2 ]
|
|
[[ "$output" == *"REFUSED"*"SHORT"* ]]
|
|
[[ "$output" == *"gram.r0.blk.0.ffn_down_exps.0.bin"* ]]
|
|
[ ! -e "$BATS_TEST_TMPDIR/no.gguf" ]
|
|
}
|
|
|
|
@test "an empty recording directory is refused" {
|
|
mkdir -p "$BATS_TEST_TMPDIR/empty"
|
|
run python3 "$TOOL" "$BATS_TEST_TMPDIR/empty" -o "$BATS_TEST_TMPDIR/no.gguf"
|
|
[ "$status" -eq 2 ]
|
|
[[ "$output" == *"REFUSED"*"NO_GRAMS"* ]]
|
|
[[ "$output" == *"$BATS_TEST_TMPDIR/empty"* ]]
|
|
[ ! -e "$BATS_TEST_TMPDIR/no.gguf" ]
|
|
}
|
|
|
|
@test "expert grams with no pooled gram for the layer are refused" {
|
|
# Without the pooled gram there is nothing for an under-threshold expert to
|
|
# inherit, and the tool would quietly invent a zero diagonal for it.
|
|
C="$BATS_TEST_TMPDIR/nopooled"
|
|
mkdir -p "$C"
|
|
make_gram "$C/gram.r0.blk.3.ffn_down_exps.7.bin" 4 0 500 0 "1,2,3,4"
|
|
run python3 "$TOOL" "$C" -o "$BATS_TEST_TMPDIR/no.gguf"
|
|
[ "$status" -eq 2 ]
|
|
[[ "$output" == *"REFUSED"*"NO_POOLED"*"layer 3"* ]]
|
|
[[ "$output" == *"gram.r<rank>.blk.3.ffn_down_exps.bin"* ]]
|
|
}
|
|
|
|
@test "the same global expert recorded twice with different numbers is refused" {
|
|
# Rank files carry GLOBAL expert numbering and so describe disjoint experts;
|
|
# a collision means the recording is corrupt, and whichever file sorted last
|
|
# would win.
|
|
C="$BATS_TEST_TMPDIR/conflict"
|
|
full_recording "$C"
|
|
make_gram "$C/gram.r1.blk.0.ffn_down_exps.0.bin" 4 1 77 0 "10,20,30,40"
|
|
run python3 "$TOOL" "$C" -o "$BATS_TEST_TMPDIR/no.gguf"
|
|
[ "$status" -eq 2 ]
|
|
[[ "$output" == *"REFUSED"*"EXPERT_CONFLICT"*"expert 0"* ]]
|
|
[[ "$output" == *"gram.r0.blk.0.ffn_down_exps.0.bin"* ]]
|
|
[[ "$output" == *"gram.r1.blk.0.ffn_down_exps.0.bin"* ]]
|
|
}
|
|
|
|
@test "an existing output is refused by name rather than overwritten" {
|
|
# The trash law: a re-run must not destroy the imatrix a convert is reading.
|
|
C="$BATS_TEST_TMPDIR/full"
|
|
full_recording "$C"
|
|
: > "$BATS_TEST_TMPDIR/keep.gguf"
|
|
printf 'do not clobber me\n' > "$BATS_TEST_TMPDIR/keep.gguf"
|
|
run python3 "$TOOL" "$C" -o "$BATS_TEST_TMPDIR/keep.gguf"
|
|
[ "$status" -eq 2 ]
|
|
[[ "$output" == *"REFUSED"*"EXISTS"*"keep.gguf"* ]]
|
|
run cat "$BATS_TEST_TMPDIR/keep.gguf"
|
|
[[ "$output" == *"do not clobber me"* ]]
|
|
}
|
|
|
|
@test "--min-rows moves the threshold and the bytes follow it" {
|
|
# The default threshold is the engine's own law (4*k rows, lq_hess.h), but
|
|
# the operator has to be able to raise it when a corpus is thin. Asserted on
|
|
# the slices, not on a report line: a flag that only changes prose is a
|
|
# flag that changes nothing.
|
|
C="$BATS_TEST_TMPDIR/floor"
|
|
full_recording "$C"
|
|
run python3 "$TOOL" "$C" -o "$BATS_TEST_TMPDIR/far.gguf" --min-rows 200
|
|
[ "$status" -eq 0 ]
|
|
# 200 is above every expert's routing (100, 3, 64, 0, 100 and layer 1's 48),
|
|
# so all six slices now inherit.
|
|
[[ "$output" == *"inherited=6"* ]]
|
|
run gguf_read "$BATS_TEST_TMPDIR/far.gguf" vals blk.0.ffn_down_exps.in_sum2 0 4
|
|
[[ "$output" == *"VALS blk.0.ffn_down_exps.in_sum2 1.5 2.5 3.5 4.5"* ]]
|
|
# The counts do not move with the threshold: routing is a measurement, not a
|
|
# judgement about how much data is enough.
|
|
run gguf_read "$BATS_TEST_TMPDIR/far.gguf" vals blk.0.ffn_down_exps.counts 0 5
|
|
[[ "$output" == *"VALS blk.0.ffn_down_exps.counts 100 3 64 0 100"* ]]
|
|
}
|