import { useCallback, useRef, useState } from "react"; import compileStanProgram, { DEFAULT_WASI_SERVER_URL, } from "./compileStanProgram"; import runRemoteChains from "./runRemoteChains"; import { ChainStatusMap, defaultSamplingOpts, MultiChainResult, SamplingOpts, } from "./types"; const DEFAULT_STAN_PROGRAM = `data { int N; array[N] int y; } parameters { real theta; } model { theta ~ beta(1, 1); y ~ bernoulli(theta); } `; const DEFAULT_DATA_JSON = `{ "N": 10, "y": [0, 1, 0, 0, 0, 0, 0, 0, 0, 1] } `; const mean = (xs: number[]) => xs.reduce((a, b) => a + b, 0) / xs.length; const std = (xs: number[]) => { const m = mean(xs); return Math.sqrt(mean(xs.map((x) => (x - m) ** 2))); }; function App() { const [stanProgram, setStanProgram] = useState(DEFAULT_STAN_PROGRAM); const [dataJson, setDataJson] = useState(DEFAULT_DATA_JSON); const [wasiServerUrl, setWasiServerUrl] = useState( () => localStorage.getItem("wasiServerUrl") ?? DEFAULT_WASI_SERVER_URL, ); const [wasmExecUrl, setWasmExecUrl] = useState( () => localStorage.getItem("wasmExecUrl") ?? "https://wasm-exec.fly.dev", ); const [wasmExecKey, setWasmExecKey] = useState( () => localStorage.getItem("wasmExecKey") ?? "", ); const [compileStatus, setCompileStatus] = useState(""); const [wasiWasmUrl, setWasiWasmUrl] = useState(undefined); const [compiling, setCompiling] = useState(false); const [samplingOpts, setSamplingOpts] = useState(defaultSamplingOpts); const [sampling, setSampling] = useState(false); const [chainStatus, setChainStatus] = useState({}); const [samplingError, setSamplingError] = useState(""); const [result, setResult] = useState(undefined); const cancelRef = useRef<(() => void) | undefined>(undefined); const handleCompile = useCallback(async () => { setCompiling(true); setResult(undefined); setWasiWasmUrl(undefined); const { artifactUrl } = await compileStanProgram( wasiServerUrl, stanProgram, setCompileStatus, ); setWasiWasmUrl(artifactUrl); setCompiling(false); }, [wasiServerUrl, stanProgram]); const handleSample = useCallback(async () => { if (!wasiWasmUrl) return; setSampling(true); setSamplingError(""); setResult(undefined); setChainStatus({}); try { // fetch the compiled WASI module from the compile server (browser // HTTP cache makes repeat samples cheap) const resp = await fetch(wasiWasmUrl); if (!resp.ok) throw new Error(`failed to fetch ${wasiWasmUrl}: ${resp.statusText}`); const moduleBytes = new Uint8Array(await resp.arrayBuffer()); const run = runRemoteChains( wasmExecUrl, wasmExecKey, moduleBytes, dataJson, samplingOpts, (chainId, status) => setChainStatus((prev) => ({ ...prev, [chainId]: status })), ); cancelRef.current = run.cancel; setResult(await run.result); } catch (e) { setSamplingError(`${e}`); } finally { cancelRef.current = undefined; setSampling(false); } }, [wasiWasmUrl, wasmExecUrl, wasmExecKey, dataJson, samplingOpts]); const handleCancel = useCallback(() => { cancelRef.current?.(); cancelRef.current = undefined; setSampling(false); setSamplingError("canceled"); }, []); const setOpt = (key: keyof SamplingOpts, value: number) => setSamplingOpts((prev) => ({ ...prev, [key]: value })); return (

Stan Remote Sampling

The Stan program is compiled to a pure-WASI module by{" "} stan-wasm-wasi; sampling runs one chain per parallel job on wasm-exec workers.

Stan program