1// The result-file format committed to the fastandaccurate-results repo.
2// One file holds one work-precision sweep: one problem instance, one
3// solver, one environment.
5import {
6 canonicalSpec,
7 canonicalSpecJson,
8 PROBLEM_ID,
9 PROBLEM_VERSION,
10 type Laplace2dInstance,
11} from "../problems/laplace2d/spec";
12import type { RunPoint } from "./runner";
14export const RESULT_FORMAT = "fastandaccurate-result";
15export const RESULT_FORMAT_VERSION = 1;
17export interface ResultEnvironment {
18 kind: "browser" | "node" | "matlab";
19 /** User agent (browser), node version (node), or MATLAB version. */
20 runtime: string;
21 /** Absent for runs outside numbl (e.g. real MATLAB). */
22 numblVersion?: string;
23 os?: string;
24 cpu?: string;
25 /** Free-text label a human recognizes ("office workstation"). */
26 machineLabel?: string;
27 /** Whether a visitor can rerun this result in the browser. */
28 browserReproducible: boolean;
29}
31export interface ResultPoint {
32 n: number;
33 /** The fastest of the timed runs. */
34 solveSeconds: number;
35 solveSecondsAll: number[];
36 coldSeconds: number;
37 relMax: number;
38 relL2: number;
39}
41export interface ResultFile {
42 format: typeof RESULT_FORMAT;
43 formatVersion: typeof RESULT_FORMAT_VERSION;
44 problem: string;
45 problemVersion: number;
46 instance: string;
47 spec: Record<string, unknown>;
48 specHash: string;
49 solver: {
50 id: string;
51 version: string;
52 backend: "cpu" | "gpu";
53 /** "builtin" for solvers in the fastandaccurate repo; otherwise a
54 * URL or free-text pointer to the solver's source. */
55 source: string;
56 };
57 environment: ResultEnvironment;
58 protocol: {
59 warmupRuns: number;
60 timedRuns: number;
61 /** Which statistic of the timed runs is reported as solveSeconds. */
62 statistic: "min";
63 timer: string;
64 };
65 createdUtc: string;
66 points: ResultPoint[];
67}
69/** SHA-256 hex digest, using WebCrypto (browser, worker, node >= 20). */
70export async function sha256Hex(text: string): Promise<string> {
71 const digest = await crypto.subtle.digest(
72 "SHA-256",
73 new TextEncoder().encode(text)
74 );
75 return Array.from(new Uint8Array(digest))
76 .map((b) => b.toString(16).padStart(2, "0"))
77 .join("");
78}
80export function toResultPoint(p: RunPoint): ResultPoint {
81 return {
82 n: p.n,
83 solveSeconds: p.solveSeconds,
84 solveSecondsAll: p.solveSecondsAll,
85 coldSeconds: p.coldSeconds,
86 relMax: p.relMax,
87 relL2: p.relL2,
88 };
89}
91export async function buildResultFile(opts: {
92 instance: Laplace2dInstance;
93 solver: { id: string; version: string; backend: "cpu" | "gpu"; source: string };
94 environment: ResultEnvironment;
95 repeats: number;
96 points: ResultPoint[];
97 /** What measured the times (default numbl tic/toc). */
98 timer?: string;
99}): Promise<ResultFile> {
100 return {
101 format: RESULT_FORMAT,
102 formatVersion: RESULT_FORMAT_VERSION,
103 problem: PROBLEM_ID,
104 problemVersion: PROBLEM_VERSION,
105 instance: opts.instance.id,
106 spec: canonicalSpec(opts.instance) as unknown as Record<string, unknown>,
107 specHash: await sha256Hex(canonicalSpecJson(opts.instance)),
108 solver: opts.solver,
109 environment: opts.environment,
110 protocol: {
111 warmupRuns: 2,
112 timedRuns: opts.repeats,
113 statistic: "min",
114 timer: opts.timer ?? "numbl tic/toc",
115 },
116 createdUtc: new Date().toISOString(),
117 points: opts.points,
118 };
119}