/ concept-collection / fastandaccurate
Sign in
concept-collection / fastandaccurate
fastandaccurate / test / solver-test.ts
103 lines · 3.8 KBCodeBlameHistory
5127df5fastandaccurate: PDE solver benchmarks, laplace-dirichlet-2d with MFS and Nystrom DLP solversJeremy Magland 1// Convergence test: run both solvers on the official instances through
2// numbl in node and check that errors behave as the theory says they
3// should. Run with: npx tsx test/solver-test.ts
5import { readFileSync } from "fs";
6import { fileURLToPath } from "url";
7import { dirname, join } from "path";
8import { INSTANCES, getInstance } from "../src/problems/laplace2d/spec";
9import { SOLVERS } from "../src/solvers";
10import { runPoint, type MatlabSources } from "../src/harness/runner";
11import { runSweep } from "../src/harness/sweep";
59058e5Add chunkie-dlp solver: mip package support in the harness, curl-backed file I/O for nodeJeremy Magland 12import { setNumblFileIO } from "../src/harness/numblRun";
13import { NodeFileIOAdapter } from "../src/cli/nodeFileIO";
15// Solvers that mip-install packages (chunkie-dlp) need file I/O; in node
16// that is the curl-backed adapter.
17setNumblFileIO((vfs) => new NodeFileIOAdapter(vfs));
19const root = join(dirname(fileURLToPath(import.meta.url)), "..");
20const read = (p: string) => readFileSync(join(root, p), "utf-8");
22const base = {
23 buildProblem: read("src/problems/laplace2d/matlab/build_problem.m"),
24 bdata: read("src/problems/laplace2d/matlab/laplace2d_bdata.m"),
25};
26const solverSources: Record<string, MatlabSources> = {
27 mfs: { ...base, solver: read("src/solvers/mfs/solver.m") },
28 "nystrom-dlp": { ...base, solver: read("src/solvers/nystrom-dlp/solver.m") },
29};
31// Best relMax each solver must reach over its full sweep. On star-hard,
32// MFS is additionally required NOT to do well: its charge curve lies
33// beyond the data's singularities there, and if it suddenly reached high
34// accuracy the instance would no longer be testing what the spec says.
35const mustReach: Record<string, Record<string, number>> = {
59058e5Add chunkie-dlp solver: mip package support in the harness, curl-backed file I/O for nodeJeremy Magland 36 mfs: { "disk-easy": 1e-12, "star-medium": 1e-12, "star-hard": 1e-2 },
37 "nystrom-dlp": { "disk-easy": 1e-10, "star-medium": 1e-10, "star-hard": 1e-8 },
39const mustNotReach: Record<string, Record<string, number>> = {
40 mfs: { "star-hard": 1e-8 },
41};
43let failures = 0;
e58e208chunkie-dlp runs in real MATLAB via the CLI; solver runtime field, matlab -batch harnessJeremy Magland 45// MATLAB-runtime solvers are covered by test/matlab-test.ts, run locally
46// where MATLAB exists; this suite tests the numbl solvers.
e58e208chunkie-dlp runs in real MATLAB via the CLI; solver runtime field, matlab -batch harnessJeremy Magland 48 for (const solver of SOLVERS.filter((s) => s.runtime === "numbl")) {
5127df5fastandaccurate: PDE solver benchmarks, laplace-dirichlet-2d with MFS and Nystrom DLP solversJeremy Magland 49 console.log(`\n== ${inst.id} / ${solver.id}`);
50 console.log(" n relMax relL2 solve(s) cold(s)");
51 let best = Infinity;
52 runSweep({
53 instance: inst,
54 solver,
55 sources: solverSources[solver.id],
56 repeats: 1,
57 onPoint: (p) => {
58 best = Math.min(best, p.relMax);
59 console.log(
60 ` ${String(p.n).padStart(4)} ${p.relMax.toExponential(3)} ` +
61 `${p.relL2.toExponential(3)} ${p.solveSeconds.toFixed(4)} ${p.coldSeconds.toFixed(4)}`
62 );
63 },
64 });
65 const reach = mustReach[solver.id][inst.id];
66 const notReach = mustNotReach[solver.id]?.[inst.id];
67 if (best > reach) {
68 console.log(` FAIL: best relMax ${best.toExponential(2)} > ${reach}`);
69 failures++;
70 } else if (notReach !== undefined && best < notReach) {
71 console.log(
72 ` FAIL: best relMax ${best.toExponential(2)} < ${notReach} ` +
73 "(instance no longer defeats this method)"
74 );
75 failures++;
76 } else {
77 console.log(` ok (best relMax ${best.toExponential(2)})`);
78 }
79 }
82// The grid path: one run with wantGrid on the medium instance.
84 const p = runPoint({
85 instance: getInstance("star-medium"),
86 n: 64,
87 repeats: 1,
88 wantGrid: true,
89 sources: solverSources["nystrom-dlp"],
90 });
91 if (!p.uGrid || p.uGrid.length !== 200 * 200) {
92 console.log(`\nFAIL: grid has ${p.uGrid?.length ?? 0} values, expected 40000`);
93 failures++;
94 } else {
95 console.log(`\ngrid ok (${p.uGrid.length} values)`);
96 }
99if (failures > 0) {
100 console.error(`\n${failures} failure(s)`);
101 process.exit(1);
103console.log("\nall checks passed");
moveopenescclose