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 文件运行;冷加载程序会启动有限数量的子进程,所以必须作为文件执行。源码仅重新排版。
C.1 构件、压缩与新进程初始化
Section titled “C.1 构件、压缩与新进程初始化”原始源码 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。