/ concept-collection / fastandaccurate
Sign in
concept-collection / fastandaccurate
fastandaccurate / test / matlab-test.ts
79 lines · 2.9 KBBlameHistoryRaw
1// Convergence test for the MATLAB-runtime solvers, run where real MATLAB
2// exists (not in CI): npx tsx test/matlab-test.ts
3// Exits quietly with a notice when no matlab is on the PATH.
4//
5// The expectations live in test/expected.ts and are keyed by source
6// directory, so mfs-mat and nystrom-dlp-mat are held to exactly the same
7// accuracy as their numbl twins in test/solver-test.ts: if a -mat curve
8// misses it, the runtime and not the method is the suspect.
10import { readFileSync } from "fs";
11import { fileURLToPath } from "url";
12import { dirname, join } from "path";
13import { getInstance } from "../src/problems/laplace2d/spec";
14import { SOLVERS, solverSourceDir, sweepNFor } from "../src/solvers";
15import {
16 matlabAvailable,
17 matlabSetup,
18 runMatlabSweep,
19} from "../src/cli/matlabRun";
20import { DEFAULT_TIMING } from "../src/harness/timing";
21import { MUST_NOT_REACH, MUST_REACH } from "./expected";
23if (!matlabAvailable()) {
24 console.log("matlab not found on PATH; skipping MATLAB solver tests");
25 process.exit(0);
28const root = join(dirname(fileURLToPath(import.meta.url)), "..");
29const read = (p: string) => readFileSync(join(root, p), "utf-8");
30const base = {
31 buildProblem: read("src/problems/laplace2d/matlab/build_problem.m"),
32 bdata: read("src/problems/laplace2d/matlab/laplace2d_bdata.m"),
33};
35let failures = 0;
36for (const solver of SOLVERS.filter((s) => s.runtime === "matlab")) {
37 const dir = solverSourceDir(solver);
38 const sources = { ...base, solver: read(`src/solvers/${dir}/solver.m`) };
39 const setup = matlabSetup(solver.id);
40 for (const [instId, reach] of Object.entries(MUST_REACH[dir] ?? {})) {
41 console.log(`\n== ${instId} / ${solver.id} (MATLAB)`);
42 console.log(" n relMax relL2 solve(s)");
43 let best = Infinity;
44 const { points, matlabVersion } = runMatlabSweep({
45 instance: getInstance(instId),
46 ns: sweepNFor(solver, instId),
47 timing: { ...DEFAULT_TIMING, minTimedRuns: 1, timeBudgetSeconds: 0 },
48 sources,
49 setup,
50 });
51 for (const p of points) {
52 best = Math.min(best, p.relMax);
53 console.log(
54 ` ${String(p.n).padStart(4)} ${p.relMax.toExponential(3)} ` +
55 `${p.relL2.toExponential(3)} ${p.solveSeconds.toFixed(4)}`
56 );
57 }
58 console.log(` (MATLAB ${matlabVersion})`);
59 const notReach = MUST_NOT_REACH[dir]?.[instId];
60 if (best > reach) {
61 console.log(` FAIL: best relMax ${best.toExponential(2)} > ${reach}`);
62 failures++;
63 } else if (notReach !== undefined && best < notReach) {
64 console.log(
65 ` FAIL: best relMax ${best.toExponential(2)} < ${notReach} ` +
66 "(instance no longer defeats this method)"
67 );
68 failures++;
69 } else {
70 console.log(` ok (best relMax ${best.toExponential(2)})`);
71 }
72 }
75if (failures > 0) {
76 console.error(`\n${failures} failure(s)`);
77 process.exit(1);
79console.log("\nall MATLAB checks passed");
moveopenescclose