DMCRA-LAB / quant.js
AEUPH's picture
DMCRA Lab: elastic level-set quantisation over real GGUF weights, measured end to end
5664efc verified
Raw History Blame Contribute Delete
21.9 kB
/* quant.js — the DMCRA level-set quantiser: a codebook chosen per block, from an elastic family.
*
* WHAT THIS IS, AND WHAT IT IS NOT
* The DMCRA document proposes replacing fixed ternary `{-1,0,+1}` with an elastic family of grids
* that "unclamps" as pressure demands. Stripped of the hardware vocabulary that a static page
* cannot implement, that is one concrete, decidable mechanism: a weight matrix is cut into blocks,
* each block is assigned a LEVEL SET from a nested family, and the assignment is chosen by a
* measured criterion under a bit budget. This file is that mechanism and nothing else. There is no
* routing fabric here, and no zero-bus: `0/0/0` is three zeros and this module reports, by exact
* arithmetic, that it reconstructs identically to `{0}`.
*
* Two scale rules, on purpose:
* rule "meanabs" — scale = mean(|w|) per block, the convention this repo's weights.js already
* uses for ternary. Reproduced here so a test can assert that this quantiser's
* ternary is IDENTICAL to the already-verified `toTernary`/`fromTernary`. That
* assertion is what stops this file from drifting away from the project.
* rule "lsq" — least-squares optimal step per block (the classic LSQ iteration, Esser et al.
* 2020): s <- sum(w*q)/sum(q*q) with q = nearest level of w/s. Default for the
* level-set ladder, because it gives EVERY grid its own optimal scale, so the
* ladder compares codebooks rather than comparing one grid's scale artefact
* against another's.
*
* Bit accounting is explicit and separated, because the interesting honest wrinkle is that they
* disagree: `indexBits` is what an encoder must carry per weight, `effBits` adds the per-block scale
* overhead (which is what makes small blocks expensive), and `entropyBits` is the measured Shannon
* entropy of the level histogram. A grid that claims five channels but decodes to three distinct
* values is reported as exactly that: `distinctLevels` < `levels.length`, `wastedChannels` > 0.
*/
(function (root, factory) {
var Q = factory();
if (typeof module !== "undefined" && module.exports) module.exports = Q;
root.DMCRAQUANT = Q;
})(typeof globalThis !== "undefined" ? globalThis : this, function () {
"use strict";
/* ---------------------------------------------------------------- the elastic family
* The document's family is nested: {+-1} -> {0,+-1} -> {0,+-1,+-2} -> ... . Two extra grids are
* included because they are what an equal-bits contest actually needs: `quad` spends the SAME two
* index bits as ternary but on two magnitudes instead of a zero, and `oct` spends the same three
* bits as penta on four magnitudes instead of a zero. Without those two, "unclamping ternary"
* would only ever be compared against a cheaper code, which would decide nothing.
* `ternaryDup` is not a proposal; it is the document's `0/0/0` written down so it can be measured.
*/
var GRIDS = {
binary: { levels: [0, 1], note: "sign only, 1 index bit" },
binaryPN: { levels: [-1, 1], note: "bipolar sign only, 1 index bit" },
ternary: { levels: [-1, 0, 1], note: "BitNet b1.58, 2 index bits" },
quad: { levels: [-2, -1, 1, 2], note: "2 magnitudes, no zero, 2 index bits" },
ternaryDup:{ levels: [-1, 0, 0, 0, 1], note: "the document's {0/0/0}: 5 channels, 3 distinct values" },
penta: { levels: [-2, -1, 0, 1, 2], note: "3 index bits" },
hepta: { levels: [-3, -2, -1, 0, 1, 2, 3], note: "3 index bits, same cost as penta" },
oct: { levels: [-4, -3, -2, -1, 1, 2, 3, 4], note: "4 magnitudes, no zero, 3 index bits" },
nona: { levels: [-4, -3, -2, -1, 0, 1, 2, 3, 4], note: "4 index bits" }
};
var SCALE_BYTES = 4;
function gridOf(grid) {
if (typeof grid === "string") {
if (!GRIDS[grid]) throw new Error("unknown grid: " + grid);
return GRIDS[grid].levels.slice();
}
if (Object.prototype.toString.call(grid) === "[object Array]") {
if (!grid.length) throw new Error("empty level set");
return grid.slice();
}
throw new Error("grid must be a known name or an array of levels");
}
function indexBitsFor(k) { return Math.max(1, Math.ceil(Math.log2(k))); }
/* Distinct DECODED values. This is what makes `ternaryDup` measurable rather than rhetorical:
* three zero channels decode to one value, so the encoding is wider than the codebook. */
function distinctValues(levels) {
var seen = Object.create(null), n = 0, i, v;
for (i = 0; i < levels.length; i++) { v = levels[i]; if (seen[v] !== 1) { seen[v] = 1; n++; } }
return n;
}
function gridInfo(grid) {
var levels = gridOf(grid);
var maxLevel = 0, i;
for (i = 0; i < levels.length; i++) if (Math.abs(levels[i]) > maxLevel) maxLevel = Math.abs(levels[i]);
var d = distinctValues(levels);
var idx = indexBitsFor(levels.length);
var dec = indexBitsFor(d);
return {
levels: levels, count: levels.length, maxLevel: maxLevel,
distinctLevels: d, distinctBits: dec,
indexBits: idx, decodeBits: dec,
wastedChannels: levels.length - d,
/* Channels paid for that the decoder cannot distinguish. Zero for every honest grid. */
wastedBits: idx - dec
};
}
/* ---------------------------------------------------------------- nearest level */
function isContiguousIntegers(levels) {
for (var i = 1; i < levels.length; i++) if (levels[i] !== levels[i - 1] + 1) return false;
return true;
}
/* Nearest level to x, given that the levels were built by scaling integers. Ties resolve to the
* first (lowest) candidate, which is why three zero channels can never disagree: every one of
* them is 0, so the reconstructed value is 0 whichever way the tie falls. */
function nearestIndex(levels, x, contiguous, lo, hi) {
if (contiguous) {
var r = Math.round(x);
if (r < lo) r = lo; else if (r > hi) r = hi;
return r - lo;
}
var best = 0, bestD = Infinity, i, d;
for (i = 0; i < levels.length; i++) {
d = Math.abs(x - levels[i]);
if (d < bestD) { bestD = d; best = i; }
}
return best;
}
/* ---------------------------------------------------------------- block statistics */
/* Per-block moments. `variance` is the document's stated pressure; it is computed here so the
* allocation experiment can test whether it is the RIGHT pressure, and `mse` per grid is kept so
* the benefit-driven allocator can allocate by measured error reduction instead. */
function blockStats(values, blockSize, gridNames, rule) {
var n = values.length;
var bs = blockSize > 0 ? blockSize : n;
var out = [], off;
for (off = 0; off < n; off += bs) {
var len = Math.min(bs, n - off);
var sum = 0, absSum = 0, sq = 0, i, v;
for (i = 0; i < len; i++) { v = values[off + i]; sum += v; absSum += Math.abs(v); sq += v * v; }
var mean = sum / len;
var variance = Math.max(0, sq / len - mean * mean);
var rec = { offset: off, len: len, mean: mean, meanAbs: absSum / len,
variance: variance, sd: Math.sqrt(variance), rms: Math.sqrt(sq / len), mse: {} };
if (gridNames) {
for (i = 0; i < gridNames.length; i++) {
var q = quantiseBlock(values, off, len, gridNames[i], rule);
rec.mse[gridNames[i]] = q.mse;
}
}
out.push(rec);
}
return out;
}
/* ---------------------------------------------------------------- the scale step */
function meanAbsOf(values, off, len) {
var s = 0, i;
for (i = 0; i < len; i++) s += Math.abs(values[off + i]);
return s / len;
}
/* LSQ step: iterate s <- sum(w*q)/sum(q*q) until it stops moving. This is the same update used in
* learned step-size quantization, run here as a few fixed-point iterations per block. */
function lsqScale(values, off, len, levels, contiguous, lo, hi, seed) {
var s = seed > 0 ? seed : meanAbsOf(values, off, len);
if (!(s > 0)) {
/* An all-zero block: any scale reconstructs it exactly. */
return 0;
}
var it, i, num, den, q, w, s2;
for (it = 0; it < 6; it++) {
num = 0; den = 0;
for (i = 0; i < len; i++) {
w = values[off + i];
q = levels[nearestIndex(levels, w / s, contiguous, lo, hi)];
num += w * q; den += q * q;
}
if (den <= 0) break;
s2 = num / den;
if (!(s2 > 0)) break;
if (Math.abs(s2 - s) <= 1e-12 * s) { s = s2; break; }
s = s2;
}
return s;
}
/* ---------------------------------------------------------------- one block */
/* Quantise one block under one grid. Returns the reconstruction, the scale, and both error and
* bit accounts. `rule` is "lsq" (default) or "meanabs" (the weights.js ternary convention). */
function quantiseBlock(values, off, len, grid, rule) {
var info = gridInfo(grid);
var levels = info.levels;
var contiguous = isContiguousIntegers(levels);
var lo = contiguous ? levels[0] : 0, hi = contiguous ? levels[levels.length - 1] : 0;
var s;
if (rule === "meanabs") s = meanAbsOf(values, off, len);
else s = lsqScale(values, off, len, levels, contiguous, lo, hi, meanAbsOf(values, off, len));
var rec = new Float32Array(len);
var counts = new Array(levels.length);
for (var c = 0; c < counts.length; c++) counts[c] = 0;
var sse = 0, sae = 0, maxAbs = 0, i, w, qi, r, d;
for (i = 0; i < len; i++) {
w = values[off + i];
qi = s > 0 ? nearestIndex(levels, w / s, contiguous, lo, hi) : 0;
counts[qi]++;
r = s > 0 ? levels[qi] * s : 0;
rec[i] = r;
d = w - r; sse += d * d; sae += Math.abs(d);
if (Math.abs(d) > maxAbs) maxAbs = Math.abs(d);
}
return {
grid: typeof grid === "string" ? grid : "custom", levels: levels, scale: s,
values: rec, counts: counts,
mse: len ? sse / len : 0, mae: len ? sae / len : 0, maxAbs: maxAbs,
bits: blockBits(len, s, info, counts)
};
}
/* Bit account for one block. `indexBits` travels per weight; the scale is one 4-byte float per
* block, so its share is 32/len bits per weight — the term that punishes small blocks. */
function blockBits(len, scale, info, counts) {
var indexBits = info.indexBits;
var scaleBits = len ? (SCALE_BYTES * 8) / len : 0;
var entropy = 0, total = len || 1, j, p;
if (counts) {
for (j = 0; j < counts.length; j++) {
if (!counts[j]) continue;
p = counts[j] / total;
entropy -= p * Math.log2(p);
}
}
return {
indexBits: indexBits, scaleBits: scaleBits, effBits: indexBits + scaleBits,
entropyBits: entropy + (scale ? scaleBits : 0),
decodeBits: info.decodeBits, wastedBits: info.wastedBits,
wastedChannels: info.wastedChannels, distinctLevels: info.distinctLevels,
channels: info.count, scaleZero: !(scale > 0)
};
}
/* ---------------------------------------------------------------- whole tensor */
/* Quantise a whole array under one grid. Returns the reconstruction plus an aggregate bit account
* that is the length-weighted mean of the per-block accounts, so a caller can compare two grids
* at equal bits rather than at equal nominal precision. */
function quantise(values, opts) {
opts = opts || {};
var grid = opts.grid === undefined ? "ternary" : opts.grid;
var rule = opts.rule === "meanabs" ? "meanabs" : "lsq";
var bs = opts.blockSize | 0;
var n = values.length;
var size = bs > 0 ? bs : n;
var out = new Float32Array(n);
var blocks = [], off, acc = null;
for (off = 0; off < n; off += size) {
var len = Math.min(size, n - off);
var q = quantiseBlock(values, off, len, grid, rule);
out.set(q.values, off);
blocks.push({ offset: off, len: len, scale: q.scale, mse: q.mse, bits: q.bits,
counts: q.counts, levels: q.levels });
acc = acc ? acc : {
indexBits: 0, scaleBits: 0, effBits: 0, entropyBits: 0, decodeBits: 0,
wastedBits: 0, wastedChannels: q.bits.wastedChannels, distinctLevels: q.bits.distinctLevels,
channels: q.bits.channels, grid: q.grid, name: typeof grid === "string" ? grid : "custom"
};
acc.indexBits += q.bits.indexBits * len / n;
acc.scaleBits += q.bits.scaleBits * len / n;
acc.effBits += q.bits.effBits * len / n;
acc.entropyBits += q.bits.entropyBits * len / n;
acc.decodeBits += q.bits.decodeBits * len / n;
acc.wastedBits += q.bits.wastedBits * len / n;
}
if (!acc) {
acc = { indexBits: 0, scaleBits: 0, effBits: 0, entropyBits: 0, decodeBits: 0,
wastedBits: 0, wastedChannels: 0, distinctLevels: 0, channels: 0,
grid: String(grid), name: typeof grid === "string" ? grid : "custom" };
}
acc.blockSize = bs; acc.rule = rule; acc.count = n;
acc.storedBytes = Math.ceil(n * acc.indexBits / 8) + blocks.length * SCALE_BYTES;
acc.floatBytes = n * 4;
acc.ratio = acc.storedBytes ? acc.floatBytes / acc.storedBytes : 0;
var err = measure(values, out);
acc.mse = err.mse; acc.rmse = err.rmse; acc.mae = err.mae; acc.maxAbs = err.maxAbs;
return { values: out, blocks: blocks, bits: acc, error: err };
}
/* ---------------------------------------------------------------- error */
function measure(orig, rec) {
var n = Math.min(orig.length, rec.length), i, d, sse = 0, sae = 0, maxAbs = 0, e0 = 0;
for (i = 0; i < n; i++) {
d = orig[i] - rec[i];
sse += d * d; sae += Math.abs(d);
if (Math.abs(d) > maxAbs) maxAbs = Math.abs(d);
e0 += orig[i] * orig[i];
}
return {
count: n, mse: n ? sse / n : 0, rmse: n ? Math.sqrt(sse / n) : 0,
mae: n ? sae / n : 0, maxAbs: maxAbs,
/* Fraction of the original energy the reconstruction retains; 1 means nothing was lost. */
energyRetained: e0 > 0 ? 1 - sse / e0 : 1
};
}
/* Exact equality of two reconstructions, used by the {0/0/0} identity test. Compares bit patterns
* rather than a tolerance, because the claim under test is identity, not closeness. */
function identical(a, b) {
if (a.length !== b.length) return false;
for (var i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
return true;
}
/* ---------------------------------------------------------------- elastic allocation */
/* The allocator. This is the DMCRA mechanism made decidable: given a bit budget, assign each block
* a grid so that measured reconstruction error is as low as possible.
*
* key "benefit" (default) — greedy by MEASURED error reduction per extra bit, using each
* block's per-grid MSE. The "pressure" is benefit/cost, not variance.
* key "variance" — the document's stated rule: rank blocks by variance and unclamp the most
* dispersed ones first, ignoring how much each block's error would actually
* improve. Implemented faithfully so it can be compared, not assumed inferior.
*
* Budget is an average of `indexBits` per weight. The greedy walks upgrades from the cheapest
* candidate upward and stops when the next cheapest upgrade no longer fits, so the realised
* average is <= budget and, when the budget happens to be reachable exactly, equals it.
*/
function allocate(values, opts) {
opts = opts || {};
var candidates = (opts.candidates || ["binary", "ternary", "penta", "hepta"]).slice();
var rule = opts.rule === "meanabs" ? "meanabs" : "lsq";
var budget = opts.budget === undefined ? 2 : opts.budget;
var key = opts.key === "variance" ? "variance" : "benefit";
var bs = opts.blockSize | 0;
var n = values.length;
var size = bs > 0 ? bs : n;
candidates.sort(function (a, b) { return gridInfo(a).indexBits - gridInfo(b).indexBits; });
var stats = blockStats(values, size, candidates, rule);
var nBlocks = stats.length;
if (!nBlocks) {
return { grids: [], stats: [], avgBits: 0, bits: null, values: new Float32Array(0),
error: measure(values, new Float32Array(0)), key: key, candidates: candidates };
}
/* Costs, and the exact error reduction each single-step upgrade would buy. */
var cost = candidates.map(function (c) { return gridInfo(c).indexBits; });
var level = stats.map(function () { return 0; }); // index into candidates
var totalWeight = 0;
stats.forEach(function (s) { totalWeight += s.len; });
function currentBits() {
var b = 0;
for (var i = 0; i < nBlocks; i++) b += cost[level[i]] * stats[i].len / totalWeight;
return b;
}
/* A plain greedy on single-step upgrades. Unclamping is monotone: a block only moves to a
* costlier grid, and only when that grid actually reduces its measured error, so a grid that
* costs more without helping is never selected. Each pass takes the single best-scoring upgrade
* that still fits the budget, which is the standard greedy for this separable problem. The two
* keys differ only in the score: benefit uses measured error reduction per extra bit, variance
* uses the block's own dispersion, which is the rule the document names. */
var guard = 0;
while (guard++ < nBlocks * candidates.length + 10) {
var best = -1, bestScore = -Infinity, i, next, gain, dCost, score;
for (i = 0; i < nBlocks; i++) {
next = level[i] + 1;
if (next >= candidates.length) continue;
gain = (stats[i].mse[candidates[level[i]]] - stats[i].mse[candidates[next]]) * stats[i].len;
if (gain <= 0) continue;
dCost = (cost[next] - cost[level[i]]) * stats[i].len / totalWeight;
if (dCost > 0 && currentBits() + dCost > budget + 1e-9) continue;
if (key === "variance") score = stats[i].variance + gain * 1e-18;
else score = dCost > 0 ? gain / dCost : gain;
if (score > bestScore) { bestScore = score; best = i; }
}
if (best < 0) break;
level[best]++;
}
/* Realise the assignment. */
var out = new Float32Array(n);
var grids = [], off = 0, i2;
var accBits = { indexBits: 0, scaleBits: 0, effBits: 0, entropyBits: 0, storedBytes: 0 };
for (i2 = 0; i2 < nBlocks; i2++) {
var st = stats[i2];
var name = candidates[level[i2]];
var q = quantiseBlock(values, st.offset, st.len, name, rule);
out.set(q.values, st.offset);
grids.push(name);
accBits.indexBits += q.bits.indexBits * st.len / n;
accBits.scaleBits += q.bits.scaleBits * st.len / n;
accBits.effBits += q.bits.effBits * st.len / n;
accBits.entropyBits += q.bits.entropyBits * st.len / n;
accBits.storedBytes += Math.ceil(st.len * q.bits.indexBits / 8) + SCALE_BYTES;
}
accBits.count = n; accBits.blockSize = bs; accBits.rule = rule;
accBits.mode = "elastic"; accBits.key = key; accBits.budget = budget;
accBits.storedBytes = Math.ceil(accBits.storedBytes);
accBits.floatBytes = n * 4;
accBits.ratio = accBits.storedBytes ? accBits.floatBytes / accBits.storedBytes : 0;
var err2 = measure(values, out);
accBits.mse = err2.mse; accBits.rmse = err2.rmse; accBits.mae = err2.mae;
accBits.maxAbs = err2.maxAbs;
var hist = Object.create(null);
grids.forEach(function (g) { hist[g] = (hist[g] || 0) + 1; });
accBits.gridHistogram = hist;
return { values: out, grids: grids, stats: stats, bits: accBits, error: err2,
avgBits: accBits.indexBits, key: key, candidates: candidates,
/* The ladder the allocator was allowed to climb, for reporting. */
costs: candidates.map(function (c) { return { grid: c, indexBits: gridInfo(c).indexBits }; }) };
}
/* ---------------------------------------------------------------- the {0/0/0} experiment */
/* Everything needed to settle the document's multi-channel-centroid claim numerically: the same
* weights quantised with one zero channel and with three, the reconstruction compared for exact
* inequality, and the channel accounting of each. A three-channel zero that decodes identically
* is not an information channel; the caller can print both rows and see that. */
function zeroChannelTest(values, opts) {
opts = opts || {};
var bs = opts.blockSize | 0;
var one = quantise(values, { grid: "ternary", blockSize: bs, rule: opts.rule });
var three = quantise(values, { grid: "ternaryDup", blockSize: bs, rule: opts.rule });
return {
one: one, three: three,
identical: identical(one.values, three.values),
info: { ternary: gridInfo("ternary"), ternaryDup: gridInfo("ternaryDup") }
};
}
/* ---------------------------------------------------------------- HDC-side helpers */
/* Shannon entropy in bits of a categorical histogram, used by the bundling experiment to compare
* multi-channel codes at equal information content rather than equal channel count. */
function entropy(counts) {
var total = 0, i;
for (i = 0; i < counts.length; i++) total += counts[i];
if (!total) return 0;
var h = 0, p;
for (i = 0; i < counts.length; i++) {
if (!counts[i]) continue;
p = counts[i] / total;
h -= p * Math.log2(p);
}
return h;
}
return {
GRIDS: GRIDS, gridInfo: gridInfo, gridOf: gridOf,
quantise: quantise, quantiseBlock: quantiseBlock, blockStats: blockStats,
allocate: allocate, zeroChannelTest: zeroChannelTest,
measure: measure, identical: identical, entropy: entropy,
indexBitsFor: indexBitsFor, SCALE_BYTES: SCALE_BYTES
};
});