1// Runs one solver at one resolution on one instance and measures it.
2// The timing protocol (see docs/problems/laplace-dirichlet-2d.md): one
3// untimed warmup run absorbs JIT compilation, then `repeats` timed runs
4// whose median is the reported solve time. The warmup itself is timed
5// and reported as the cold time. All timing is MATLAB tic/toc inside the
6// numbl session, so browser and node measure the same thing.
8import { runNumblScript } from "./numblRun";
9import type { Laplace2dInstance } from "../problems/laplace2d/spec";
10import { evalErrors } from "../problems/laplace2d/exact";
12export interface MatlabSources {
13 /** build_problem.m source */
14 buildProblem: string;
15 /** laplace2d_bdata.m source */
16 bdata: string;
17 /** laplace2d_bdata_branch.m source */
18 bdataBranch: string;
19 /** solver.m source of the solver under test */
20 solver: string;
21}
23export interface RunPointRequest {
24 instance: Laplace2dInstance;
25 n: number;
26 /** Timed repeats after the warmup (default 3). */
27 repeats?: number;
28 /** Also evaluate the solution on the visualization grid. */
29 wantGrid?: boolean;
30 sources: MatlabSources;
31}
33export interface RunPoint {
34 n: number;
35 solveSeconds: number;
36 solveSecondsAll: number[];
37 coldSeconds: number;
38 relMax: number;
39 relL2: number;
40 uEval: Float64Array;
41 uGrid: Float64Array | null;
42}
44function numLiteral(x: number): string {
45 if (!Number.isFinite(x)) throw new Error(`bad numeric literal: ${x}`);
46 return String(x);
47}
49export function runPoint(req: RunPointRequest): RunPoint {
50 const { instance, n } = req;
51 const repeats = req.repeats ?? 3;
52 const wantGrid = req.wantGrid ?? false;
53 const family = instance.family === "branch-point" ? 1 : 0;
54 const main = [
55 "% generated by the fastandaccurate harness",
56 `prob = build_problem(${numLiteral(instance.a)}, ${numLiteral(instance.k)}, ${numLiteral(instance.d)}, ${family}, ${wantGrid ? 1 : 0});`,
57 `tic; out = solver(prob, ${n}); res_cold = toc;`,
58 `res_times = zeros(${repeats}, 1);`,
59 `for irep = 1:${repeats}`,
60 ` tic; out = solver(prob, ${n}); res_times(irep) = toc;`,
61 "end",
62 "res_ueval = out.uEval;",
63 "res_ugrid = out.uGrid;",
64 "",
65 ].join("\n");
66 const { vars } = runNumblScript(
67 main,
68 {
69 "build_problem.m": req.sources.buildProblem,
70 "laplace2d_bdata.m": req.sources.bdata,
71 "laplace2d_bdata_branch.m": req.sources.bdataBranch,
72 "solver.m": req.sources.solver,
73 },
74 ["res_cold", "res_times", "res_ueval", "res_ugrid"]
75 );
76 const times = Array.from(vars.res_times);
77 const sorted = [...times].sort((x, y) => x - y);
78 const median =
79 sorted.length % 2 === 1
80 ? sorted[(sorted.length - 1) / 2]
81 : (sorted[sorted.length / 2 - 1] + sorted[sorted.length / 2]) / 2;
82 const { relMax, relL2 } = evalErrors(instance, vars.res_ueval);
83 return {
84 n,
85 solveSeconds: median,
86 solveSecondsAll: times,
87 coldSeconds: vars.res_cold[0],
88 relMax,
89 relL2,
90 uEval: vars.res_ueval,
91 uGrid: wantGrid ? vars.res_ugrid : null,
92 };
93}