跳转到内容

HiGHS 分发与持久模型实验源码

研究总览 · 2026-09-20

附录 C:发布构件身份、冷加载及持久更新的复现材料。

附录 C:最终选定分发的复现源码

Section titled “附录 C:最终选定分发的复现源码”

从 §12.5 指定发布包仅解出 build/highs.js、build/highs.wasm 至临时目录 aira-highs-standalone-research-20260920,将二者平放。核对本文 SHA-256 后运行;不执行 npm install,不改项目锁文件。下面两个程序各保存到 TEMP 的 .cjs 文件运行;冷加载程序会启动有限数量的子进程,所以必须作为文件执行。源码仅重新排版。

原始源码 SHA-256:4178708109dbe0a7eea4bc4001030f2362b8686ea07b002ddb0c4a6f6dc7e3b6。

const fs = require("node:fs"),
os = require("node:os"),
path = require("node:path"),
cp = require("node:child_process"),
zlib = require("node:zlib"),
crypto = require("node:crypto");
const caDir =
"<workspace-root>/packages/aira-geometry-kernel/node_modules/@casadi/casadi-wasm",
hiDir = path.join(os.tmpdir(), "aira-highs-standalone-research-20260920");
async function child(mode) {
const rss0 = process.memoryUsage().rss,
t0 = performance.now();
if (mode === "casadi") {
const factory = require(path.join(caDir, "casadi.js"));
const ca = await factory();
const t1 = performance.now();
await ca.load_conic("highs");
const t2 = performance.now();
console.log(
JSON.stringify({
mode,
core_ms: t1 - t0,
plugin_ms: t2 - t1,
ready_ms: t2 - t0,
rss_delta: process.memoryUsage().rss - rss0,
}),
);
} else {
const factory = require(path.join(hiDir, "highs.js"));
const hi = await factory();
const t1 = performance.now();
console.log(
JSON.stringify({
mode,
ready_ms: t1 - t0,
version: hi.version,
memoryBytes: hi.memoryBytes,
rss_delta: process.memoryUsage().rss - rss0,
}),
);
}
}
const stat = (a) => {
a.sort((a, b) => a - b);
return {
n: a.length,
median: a[Math.floor(a.length / 2)],
p95: a[Math.ceil(a.length * 0.95) - 1],
min: a[0],
max: a[a.length - 1],
};
};
if (process.argv[2])
child(process.argv[2]).catch((e) => {
console.error(e);
process.exit(1);
});
else {
const records = [];
for (let i = 0; i < 9; i++)
for (const mode of i % 2 ? ["standalone", "casadi"] : ["casadi", "standalone"]) {
const t = performance.now(),
p = cp.spawnSync(process.execPath, [__filename, mode], {
encoding: "utf8",
maxBuffer: 1024 * 1024,
});
if (p.status !== 0) throw new Error(p.stderr);
const last = p.stdout
.trim()
.split(/\r?\n/)
.filter((l) => l.startsWith("{"))
.at(-1);
records.push({ ...JSON.parse(last), process_wall_ms: performance.now() - t });
}
const fileNames = {
casadi: ["casadi.js", "casadi_wasm.js", "casadi_wasm.wasm", "libcasadi_conic_highs.so"],
standalone: ["highs.js", "highs.wasm"],
},
files = {};
for (const [kind, names] of Object.entries(fileNames)) {
files[kind] = names.map((name) => {
const b = fs.readFileSync(path.join(kind === "casadi" ? caDir : hiDir, name));
return {
name,
bytes: b.length,
gzip9: zlib.gzipSync(b, { level: 9 }).length,
brotli11: zlib.brotliCompressSync(b, {
params: { [zlib.constants.BROTLI_PARAM_QUALITY]: 11 },
}).length,
sha256: crypto.createHash("sha256").update(b).digest("hex"),
};
});
}
const summaries = {};
for (const mode of ["casadi", "standalone"]) {
const rr = records.filter((r) => r.mode === mode);
summaries[mode] = Object.fromEntries(
[
"ready_ms",
"process_wall_ms",
"rss_delta",
...(mode === "casadi" ? ["core_ms", "plugin_ms"] : []),
].map((k) => [k, stat(rr.map((r) => r[k]))]),
);
}
console.log(
JSON.stringify(
{
environment: { node: process.version, platform: process.platform, cpu: os.cpus()[0].model },
method:
"9 fresh Node processes per distribution, alternating order, no warmup discarded; local FS OS page cache not cleared; no browser/network; ready includes require and factory plus load_conic(highs) for CasADi; gzip9/brotli11 simulated file compression, not server transfer",
records,
summaries,
files,
},
null,
2,
),
);
}

C.2 持久模型、功能、增量和证据

Section titled “C.2 持久模型、功能、增量和证据”

原始源码 SHA-256:a40a8e9a71cbd9c18e53fd3400f0973fa6210cb7428ac9ebf7e11bf26d2124c4。

const path = require("node:path"),
os = require("node:os");
const load = require(path.join(os.tmpdir(), "aira-highs-standalone-research-20260920", "highs.js"));
const stats = (a) => {
a = [...a].sort((x, y) => x - y);
return {
n: a.length,
median_ms: a[Math.floor(a.length / 2)],
p95_ms: a[Math.ceil(a.length * 0.95) - 1],
min_ms: a[0],
max_ms: a.at(-1),
};
};
(async () => {
const h = await load(),
inf = h.infinity;
const make = (n, rows, c, cl, cu) => {
const cols = Array.from({ length: n }, () => []);
rows.forEach((r, k) => r.v.forEach(([j, a]) => cols[j].push([k, a])));
const starts = [],
indices = [],
values = [];
for (const col of cols) {
starts.push(indices.length);
for (const [k, a] of col) {
indices.push(k);
values.push(a);
}
}
starts.push(indices.length);
return {
numCols: n,
numRows: rows.length,
colCost: c,
colLower: cl,
colUpper: cu,
rowLower: rows.map((r) => r.l ?? -inf),
rowUpper: rows.map((r) => r.u ?? inf),
matrix: { format: "csc", numRows: rows.length, numCols: n, starts, indices, values },
};
};
const create = (data) => {
const m = h.createModel(data);
m.options.set({ output_flag: false, solver: "simplex" });
return m;
};
const report = { version: h.version, intBits: h.intBits, functional: [] };
for (const [name, bounds, integer, quad] of [
["LP equality", [5, 5], false, false],
["MIP equality", [5, 5], true, false],
["MIP lower", [5, inf], true, false],
["convex QP", [-inf, inf], false, true],
["MIQP", [-inf, inf], true, true],
]) {
const data = make(1, [{ v: [[0, 2]], l: bounds[0], u: bounds[1] }], [quad ? -5 : 1], [0], [5]);
if (integer) data.integrality = [1];
if (quad)
data.hessian = {
format: "triangular",
dimension: 1,
starts: [0, 1],
indices: [0],
values: [2],
};
const m = h.createModel(data);
m.options.set({ output_flag: false });
try {
const run = m.run();
report.functional.push({
name,
run,
x: run.modelStatus === 7 ? Array.from(m.getSolution().colValue) : undefined,
objective: run.modelStatus === 7 ? m.getObjectiveValue() : undefined,
ray: Array.from(m.getDualRay()?.values ?? []),
});
} catch (e) {
report.functional.push({ name, error: String(e) });
} finally {
m.dispose();
m.dispose();
let disposedError;
try {
m.getModelStatus();
} catch (e) {
disposedError = e.name;
}
report.functional.at(-1).disposed = m.disposed;
report.functional.at(-1).disposedError = disposedError;
}
}
let seed = 1831565813;
const rand = () => {
seed ^= seed << 13;
seed ^= seed >>> 17;
seed ^= seed << 5;
return (seed >>> 0) / 4294967296;
};
const n = 240,
rc = 480,
rows = Array.from({ length: rc }, () => {
const seen = new Set(),
v = [];
while (v.length < 6) {
const j = Math.floor(rand() * n);
if (!seen.has(j)) {
seen.add(j);
v.push([j, 0.2 + rand() * 1.8]);
}
}
return { v, u: v.reduce((a, [, c]) => a + c * 0.5, 0) + 0.1 + rand() * 0.4 };
}),
c = Array.from({ length: n }, () => -0.5 - rand()),
data = make(n, rows, c, Array(n).fill(0), Array(n).fill(1));
const models = {
rebuild: create(data),
incremental: create(data),
rebuild_setBasis: create(data),
},
times = {},
iterations = {};
for (const [mode, m] of Object.entries(models)) {
if (m.run().modelStatus !== 7) throw new Error("initial");
times[mode] = [];
iterations[mode] = [];
}
let maxViolation = 0,
maxDifference = 0;
for (let k = 0; k < 40; k++) {
const row = (k * 17) % rc,
col = (k * 13) % n;
data.rowUpper[row] += k % 2 ? 0.015 : -0.015;
data.colCost[col] += k % 2 ? 0.003 : -0.003;
const results = {},
modes = Object.keys(models);
for (const mode of k % 2 ? modes : [...modes].reverse()) {
const m = models[mode],
t = performance.now();
if (mode === "incremental") {
m.changeRowBounds(row, -inf, data.rowUpper[row]);
m.changeColCost(col, data.colCost[col]);
} else if (mode === "rebuild_setBasis") {
const basis = m.getBasis();
m.passModel(data);
m.setBasis(basis);
} else m.passModel(data);
if (m.run().modelStatus !== 7) throw new Error("solve");
const ms = performance.now() - t,
x = m.getSolution().colValue;
let violation = 0;
for (let j = 0; j < n; j++) violation = Math.max(violation, -x[j], x[j] - 1);
for (let r = 0; r < rc; r++)
violation = Math.max(
violation,
rows[r].v.reduce((s, [j, a]) => s + a * x[j], 0) - data.rowUpper[r],
);
maxViolation = Math.max(maxViolation, violation);
results[mode] = Array.from(x).reduce((s, v, j) => s + v * data.colCost[j], 0);
if (m.info.get("basis_validity") !== 1) throw new Error("basis");
if (k >= 5) {
times[mode].push(ms);
iterations[mode].push(m.info.get("simplex_iteration_count"));
}
}
maxDifference = Math.max(
maxDifference,
Math.abs(results.rebuild - results.incremental),
Math.abs(results.rebuild - results.rebuild_setBasis),
);
}
Object.values(models).forEach((m) => m.dispose());
report.lp = {
n,
rows: rc,
nnz: 2880,
seed: 1831565813,
presolve: "choose",
maxViolation,
maxObjectiveDifference: maxDifference,
modes: Object.fromEntries(
Object.keys(models).map((mode) => [
mode,
{
...stats(times[mode]),
medianIterations: [...iterations[mode]].sort((a, b) => a - b)[17],
maxIterations: Math.max(...iterations[mode]),
},
]),
),
};
const rn = 120,
rr = Array.from({ length: rn }, (_, j) => ({
v: [
[j, -1],
[(j + 1) % rn, 1],
],
u: -1,
})),
rd = make(rn, rr, Array(rn).fill(0), Array(rn).fill(-inf), Array(rn).fill(inf)),
rt = [];
let rayCheck;
for (let k = 0; k < 40; k++) {
const m = create(rd);
if (m.run().modelStatus !== 8) throw new Error("cycle");
const t = performance.now(),
ray = m.getDualRay(),
ms = performance.now() - t;
if (!ray) throw new Error("missing ray");
if (ray.values.length !== rn) throw new Error("ray dimension");
let y = Array.from(ray.values);
if (y.reduce((a, b) => a + b, 0) < 0) y = y.map((v) => -v);
rayCheck = {
min: Math.min(...y),
bDot: -y.reduce((a, b) => a + b, 0),
maxAtResidual: Math.max(...y.map((v, j) => Math.abs(y[(j + rn - 1) % rn] - v))),
};
m.dispose();
if (k >= 5) rt.push(ms);
}
report.ray = { ...stats(rt), rayCheck };
console.log(JSON.stringify(report, null, 2));
})();

功能表仅在 modelStatus=Optimal 时记录解;不可行状态的旧缓冲区不是有效解。getDualRay() 返回的 NumericVector 通过 .values 读取,不能对外层对象直接使用 Array.from。数值循环只用于隔离研究;一般双边约束的证书转换仍遵循 §6。